tag:blogger.com,1999:blog-37628023754296125642024-03-04T21:37:12.597-10:00url::Encode | (andstuff)Blog of Titus Stone; Part programmer, part... wait... what?titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-3762802375429612564.post-58923929062468048842010-09-09T19:08:00.002-10:002010-09-09T19:08:08.871-10:00TumblrMoved to tumblr: <a href="http://urlencode.tumblr.com/">http://urlencode.tumblr.com/</a>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-11838172464645959742010-08-20T06:44:00.003-10:002010-08-27T04:40:26.239-10:00Restoring Browser Functions to Clickable DIVs Without JavascriptOccasionally the situation comes up in web development where you have a highly styled tag such as <br />
<div> or <li> that you want to make clickable. This often happens when you have a list of items in which you want to make the entire item clickble instead of just the title for example. The standard fare for doing so is to set { cursor: pointer; } with CSS then to bind the .click event to the tag (in jQuery as so)....<br />
<br />
<pre class="code">$(<span style="color: #4070a0;">'#myDivTag'</span>).click(<span style="color: #007020; font-weight: bold;">function</span>() {
<span style="color: #60a0b0; font-style: italic;">// your code here</span>
<span style="color: #60a0b0; font-style: italic;"></span>});
</pre><br />
Don't get me wrong -- this does work. However, in doing this what you loose is the standard functionality that most viewers are used to; things like middle clicking to open in a new tab, right-click to copy URL address, and so on. There is also the lag between when the page renders visibly to the user but the javascript hasn't downloaded and $(document).ready hasn't yet fired. In this case the tag will not be clickable but the mouse cursor will indicate that it should be.<br />
<br />
I've been dealing with this more so lately and there are a couple of ways to solve this problem.<br />
<br />
The first is to wrap the <div> (or li or whatever else) tag in an anchor tag <a> and set that anchor tag to { display: block; }.<br />
<br />
<pre class="code"><span style="color: #062873; font-weight: bold;"><style </span><span style="color: #4070a0;">type="text/css"</span><span style="color: #062873; font-weight: bold;">></span>
<span style="color: #06287e;">#myDivWrapper</span> { <span style="color: #007020; font-weight: bold;">display</span><span style="color: #666666;">:</span> <span style="color: #007020; font-weight: bold;">block</span>; }
<span style="color: #06287e;">#myDiv</span> { }
<span style="color: #062873; font-weight: bold;"></style></span>
<span style="color: #062873; font-weight: bold;"><a</span> <span style="color: #4070a0;">id="myDivWrapper"</span><span style="color: #062873; font-weight: bold;">></span>
<span style="color: #062873; font-weight: bold;"><div</span> <span style="color: #4070a0;">id="myDiv"</span><span style="color: #062873; font-weight: bold;">></span>
... content ...
<span style="color: #062873; font-weight: bold;"></div></span>
<span style="color: #062873; font-weight: bold;"></a></span>
</pre><br />
By wrapping the div tag in an anchor you've restored the default functionality viewers are used to with a clickable item. This works in most browsers but for the most part things like <div> and <li> are not allowed inside of an anchor tag per HTML language specifications. In a sense in doing something like this you are rolling the dice that the next version of IE9 strict mode won't render when it hits this.<br />
<br />
It also feels kind of weird, because all of the styling that was on #myDiv, things like height, width, float, etc., now need to be applied to the anchor tag instead. And no one likes wrappers. They just clutter things up.<br />
<br />
The 2nd way to solve this problem which I've started using lately is to have an absolutely positioned anchor overlay the area of the div. Consider the following...<br />
<br />
<pre class="code"><span style="color: #062873; font-weight: bold;"><style </span><span style="color: #4070a0;">type="text/css"</span><span style="color: #062873; font-weight: bold;">></span>
<span style="color: #06287e;">#myDiv</span> { <span style="color: #007020; font-weight: bold;">position</span><span style="color: #666666;">:</span> <span style="color: #007020; font-weight: bold;">relative</span>; }
<span style="color: #062873; font-weight: bold;">a</span><span style="color: #0e84b5; font-weight: bold;">.clickable-overlay</span> {
<span style="color: #007020; font-weight: bold;">position</span><span style="color: #666666;">:</span> <span style="color: #007020; font-weight: bold;">absolute</span>;
<span style="color: #007020; font-weight: bold;">top</span><span style="color: #666666;">:</span> <span style="color: #40a070;">0</span>;
<span style="color: #007020; font-weight: bold;">left</span><span style="color: #666666;">:</span> <span style="color: #40a070;">0</span>;
<span style="color: #007020; font-weight: bold;">height</span><span style="color: #666666;">:</span> <span style="color: #40a070;">100%</span>;
<span style="color: #007020; font-weight: bold;">width</span><span style="color: #666666;">:</span> <span style="color: #40a070;">100%</span>;
<span style="color: #007020; font-weight: bold;">z-index</span><span style="color:
#666666;">:</span> <span style="color: #40a070;">1</span>;
<span style="color: #007020; font-weight: bold;">display</span><span style="color:
#666666;">:</span> <span style="color: #40a070;">block</span>;
<span style="color: #007020; font-weight: bold;">background-color</span><span style="color: #666666;">:</span> <span style="color: #007020; font-weight: bold;">transparent</span>;
}
<span style="color: #062873; font-weight: bold;"></style></span>
<span style="color: #062873; font-weight: bold;"><div</span> <span style="color: #4070a0;">id="myDiv"</span><span style="color: #062873; font-weight: bold;">></span>
... content ...
<span style="color: #062873; font-weight: bold;"><a</span> <span style="color: #4070a0;">class="clickable-overlay"</span> <span style="color: #4070a0;">href="#wherever"</span><span style="color: #062873; font-weight: bold;">></a></span>
<span style="color: #062873; font-weight: bold;"></div></span>
</pre><br />
Here's what's happening: At the end of the <div> (or li or whatever) we're adding an anchor tag. The parent div is set to { position: relative }, and the anchor tag to { position: absolute; }. If you're not familiar with position absolute you're missing out. The way it is designed to work is that any element positioned absolutely is done so in relation to it's parent (or any parent's parent) that is positioned either relatively or absolutely. What this means is that in setting our <div> tag to position: relative the anchor tag with position absolute will be positioned relative to the div. Top 0 and left 0 will be the top left of the div, and height 100%, width 100% will cause the anchor to fill out the area of the div.<br />
<br />
The result is that we are left with a transparent anchor tag that completely overlays the area of the div, providing the default clickable behavior users expect. The last CSS trick to making this work is to set z-index to 1, forcing the browser to always render the anchor tag <i>over</i> whatever content is in the div.<br />
<br />
The positives to this method are that 1. we restore the expected clickable behavior without javascript (meaning it also works as soon as that markup is rendered and not when $(document).ready is fired) and 2. we're using correct HTML syntax.<br />
<br />
I should point out there is a downside to this method, in that because the anchor overlays the div, the contents of the div are no longer selectable or clickable. Depending on your application this may be negligible.titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com2tag:blogger.com,1999:blog-3762802375429612564.post-80655889349298625052010-08-12T17:27:00.000-10:002010-08-12T17:27:47.693-10:00Top 10 Programming T-Shirts That Don't Make Sense<b><span class="Apple-style-span" style="font-size: x-large;">mind.location = unknown</span></b><br />
<a href="http://www.zazzle.com/javascript_mind_tshirt-235892961444457742">http://www.zazzle.com/javascript_mind_tshirt-235892961444457742</a><br />
I wonder what the value of the variable unknown is?<br />
<br />
<b><span class="Apple-style-span" style="font-size: x-large;">import_soul</span></b><br />
<a href="http://www.zazzle.com/import_soul_tshirt-235715645595867505">http://www.zazzle.com/import_soul_tshirt-235715645595867505</a><br />
<div>What is import_soul? It's written as a keyword. If it were a function it should be import_soul(), however the keywords for this shirt include "python" which makes me thinking they <i>meant</i> <b><i>import soul</i></b>.</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">Function Check_Drunk()</span></b></div><div><a href="http://www.zazzle.com/function_check_drunk_tshirt-235954739548677347">http://www.zazzle.com/function_check_drunk_tshirt-235954739548677347</a></div><div>Where do I begin with this one... drink is treated as a global variable but never declared globally. Then there's the awesome conditional if "bra = true" which will always return true because a single = is assignment not evaluation. Lastly, none of the functions are ever called, they're just declarations of.</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">Programmers Cool Club</span></b></div><div><a href="http://www.zazzle.com/cool_programmers_club_tshirt-235068746346060145">http://www.zazzle.com/cool_programmers_club_tshirt-235068746346060145</a></div><div>No comment needed.</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">.ninja { color: black; }</span></b></div><div><a href="http://www.zazzle.com/css_ninja_style_class_tshirt-235091799113607617">http://www.zazzle.com/css_ninja_style_class_tshirt-235091799113607617</a></div><div>This one's amusing and almost worth getting, but the behavior of <i>visibility: hidden;</i> is such that it keeps screen space allocated for the element and simply does not render it. I personally believe a true ninja would <i>{ display: none; }</i>.</div><div><br />
</div><div><span class="Apple-style-span" style="font-size: x-large;"><b>Geek Power</b></span></div><div><a href="http://www.zazzle.com/geek_power_tshirt-235232870665206831">http://www.zazzle.com/geek_power_tshirt-235232870665206831</a></div><div>No geek would ever intentionally rip the cord to their keyboard like that</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">Computers are Only Human</span></b></div><div><a href="http://www.zazzle.com/computers_are_only_human_tshirt-235670270460766159">http://www.zazzle.com/computers_are_only_human_tshirt-235670270460766159</a></div><div>Apparently I didn't get that memo.</div><div><br />
</div><div><span class="Apple-style-span" style="font-size: x-large;"><b>SELECT TOP 25</b></span></div><div><a href="http://www.zazzle.com/the_best_sql_query_ever_tshirt-235031072069340726">http://www.zazzle.com/the_best_sql_query_ever_tshirt-235031072069340726</a></div><div>Aside from using the vendor-specific "TOP 25" (MS SQL), the comment at the bottom brings to light the poor execution of this SQL: It should be sorted DESCENDING</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">Do You Speak Code</span></b></div><div><a href="http://www.zazzle.com/do_you_speak_code_tshirt-235290525576102199">http://www.zazzle.com/do_you_speak_code_tshirt-235290525576102199</a></div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">There's No Crying in Programming</span></b></div><div><a href="http://www.zazzle.com/no_crying_in_programming_card-137314447034339159">http://www.zazzle.com/no_crying_in_programming_card-137314447034339159</a></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-67400004987348773682010-08-06T20:20:00.001-10:002010-08-06T20:29:59.875-10:00Python Templating For the Web (Random Ideas)Well it's past midnight and I'm musing about web templating in Python for Tornado or AppEngine. The thing about most Python templating engines that I notice is...<br />
<br />
<ul><li>There's kind of an obsession with being agnostic about everything. You can render JSON and XML and whatever else you want with our engine! When is someone just going to make a really awesome templating engine <i>for the web</i> and not try to be everything to everyone?<br />
</li>
<li>The value in a template engine to me is how easy it is to allow the template to include logic that's independent of the controller that's generating it. For example, you might want a server-side ad manager on every page, but you don't necessarily want to put the code for that in every controller. Solution? Some engines call it modules, some call it template tags, but whatever the name there is some type of logic that's independent of the controller. The problem is that most of these pieces of independent logic are needlessly complicated. (side note: Django has gotten a bit better about this since I first used it two years ago).<br />
</li>
<li>Maybe some better support for lazy-loaded values which are available for all templates... things like static URL, etc.</li>
</ul><div>So it's late and I'm thinking, what if there was a template engine, and I mean one specifically for the web, which represented an HTML document more so as a Python class. Every root element or element with an ID is a property of the document. Tags can interact with each other... Maybe this is turning out to be too much of a DOM implementation in Python. Maybe this is a bad idea. Or maybe I should try coding up a prototype just to see what it's like.</div><div><br />
</div><div>But it's not quite a DOM. The DOM is concerned with specific structure, where as we're only concerned with a loose abstract structure. It doesn't matter of the area where scripts are printed too is in the or right above , what's important is that there is a scripts area which sub-modules can access.</div><div><br />
</div><div>Instead of {% extends "some_html_file.html %}, it could be {% extends templates.master %} where templates.master is a python module.<br />
<br />
<br />
(15 minutes later)... you know it kind of sounds like I'm trying to re-invent ASP.NET WebForms...</div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-40029277910818964612010-07-27T19:34:00.002-10:002010-07-27T19:35:39.842-10:00Komodo EditI like Komodo Edit. It's simple but at the same time functional enough to be useful. It also does code completion, supports Python, and is cross-platform. (Side note: If only it supported Scala, then it would be like the <i><span class="Apple-style-span" style="font-size: x-small;">**aaaaahhh**</span></i> perfect editor).<br />
<br />
I've had this idea kicking around for a while of starting a Komodo color themes/schemes website. The idea is simple. You create a theme, then share it on the site. Other people can download it or Vote/"Like" it. Most-liked themes bubble to the top. I'm thinking of a simple architecture using Google App Engine (python) + Tornado. We'll see.<br />
<br />
In the mean time I sure did waste like 2 hours of what should have been productive programming time putting together a bunch of themes for Komodo Edit. Here are some previews of them...<br />
<br />
<br />
<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">"Heart Dry"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3M55bVhc4LimytneqSygal2DF5t0fFIaVfu0ipbHCpSoKLJhOvBLcqYRrs6_ro8dwbih3RAwN3-ndYMqgVJCyzT9KG7357NoHX9GNPTsSXm9GpavSRd1G2cYGYRGNzalaFIPLIt5f6WE/s1600/HeartDry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3M55bVhc4LimytneqSygal2DF5t0fFIaVfu0ipbHCpSoKLJhOvBLcqYRrs6_ro8dwbih3RAwN3-ndYMqgVJCyzT9KG7357NoHX9GNPTsSXm9GpavSRd1G2cYGYRGNzalaFIPLIt5f6WE/s400/HeartDry.png" width="400" /></a></div><br />
<br />
<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">"Fancy Terminal"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo4riCgWyf1RYfAhyHyUYPvOM9LBlNE184bzuqk5jnTuJoKr5g9QvAAFsOry8Hr02Gv0AV2D_X3NxE_ErIT3Kj1td8Dr8HpZ6uU5NvvJkvrugJopSRZ7D3s7i1O_Q7yNzeFyYrur0RIgs/s1600/FancyTerminal.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="117" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo4riCgWyf1RYfAhyHyUYPvOM9LBlNE184bzuqk5jnTuJoKr5g9QvAAFsOry8Hr02Gv0AV2D_X3NxE_ErIT3Kj1td8Dr8HpZ6uU5NvvJkvrugJopSRZ7D3s7i1O_Q7yNzeFyYrur0RIgs/s400/FancyTerminal.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div style="text-align: center;"> <span class="Apple-style-span" style="font-size: x-large;">"Happy Day"</span> <span class="Apple-style-span" style="font-size: small;">(currently used)</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZeOnxLzW5-qpbcuAK3_O_ZT5nPaxmMgiS5PdqaAfdiPaYWS_MDTxJYMsdCzavgcGgXHrqI2i2zpVvZBTe5n5o6l0rZW6GzgjjvIo2DHUdJ8AKqPn-8dMqVouC0gy6IXkP01QF2bDzaIE/s1600/HappyDay.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZeOnxLzW5-qpbcuAK3_O_ZT5nPaxmMgiS5PdqaAfdiPaYWS_MDTxJYMsdCzavgcGgXHrqI2i2zpVvZBTe5n5o6l0rZW6GzgjjvIo2DHUdJ8AKqPn-8dMqVouC0gy6IXkP01QF2bDzaIE/s400/HappyDay.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;"> "Monokai"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkMEgV3Ab1bE7ROnPDlHMZ-ttHV5hLwzIzPCi_W4pDW9nfJyJOswoqBa0Va6dbfbQn7c6cMsWsOMKc3H4YIpHHtFel7pj98cQJZgveykzEHOEYb9bQf6zVbMUXWFk3cMNRM1ftrKDZx44/s1600/Monokai.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkMEgV3Ab1bE7ROnPDlHMZ-ttHV5hLwzIzPCi_W4pDW9nfJyJOswoqBa0Va6dbfbQn7c6cMsWsOMKc3H4YIpHHtFel7pj98cQJZgveykzEHOEYb9bQf6zVbMUXWFk3cMNRM1ftrKDZx44/s400/Monokai.png" width="400" /></a></div><br />
<br />
<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">"Phoenix"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV7Bqz9_RRnDPx1-OMH5vVAwrd_m8ucOMWRcJ2XhUTkcOxfWAyM9o9q5vZnyMPDcRsHdndyTEg1bj_esQJyXpbXo2gyM0xsWb9auT3tLAyUA9KjgtQQQFqhTOx5OG8OpAnjnquHAW-zbY/s1600/Phoenix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV7Bqz9_RRnDPx1-OMH5vVAwrd_m8ucOMWRcJ2XhUTkcOxfWAyM9o9q5vZnyMPDcRsHdndyTEg1bj_esQJyXpbXo2gyM0xsWb9auT3tLAyUA9KjgtQQQFqhTOx5OG8OpAnjnquHAW-zbY/s400/Phoenix.png" width="400" /></a></div><br />
<br />
<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">"Transformers"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTnXuS0n8H1HML6WKMBomw5KAoInuJ9V1mjXqgeoyqR2QQGX8m-gUc3HYru0oN37Fh4eF4x6DWhuVizWRQbhrOfzGIKhCDmb_m_nVwnUWiP3d3f7OB3T4MaxSV2yXh1cREb6Jgs_dFMR0/s1600/Transformers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="113" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTnXuS0n8H1HML6WKMBomw5KAoInuJ9V1mjXqgeoyqR2QQGX8m-gUc3HYru0oN37Fh4eF4x6DWhuVizWRQbhrOfzGIKhCDmb_m_nVwnUWiP3d3f7OB3T4MaxSV2yXh1cREb6Jgs_dFMR0/s400/Transformers.png" width="400" /></a></div><br />
<br />
<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">"Work Scheme"</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvu0B2AZV6lGAVDTHro1yQCmb8LzyHLtXdXe9rhyphenhyphenHdKzIkZDqSIwesKbavK9TcqbLoCyCLSiGbOtlj2SAGJScptvva-5hRwEw5GVM2BguUQBo4dcBIsVOyZeZlFgL8PUMoqM1l59UH1WI/s1600/WorkScheme.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvu0B2AZV6lGAVDTHro1yQCmb8LzyHLtXdXe9rhyphenhyphenHdKzIkZDqSIwesKbavK9TcqbLoCyCLSiGbOtlj2SAGJScptvva-5hRwEw5GVM2BguUQBo4dcBIsVOyZeZlFgL8PUMoqM1l59UH1WI/s400/WorkScheme.png" width="400" /></a></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com3tag:blogger.com,1999:blog-3762802375429612564.post-79905642761293293092010-06-04T23:58:00.001-10:002010-06-04T23:59:45.008-10:00Formatting Console Output in ScalaSo it's midnight and I'm programming Scala (heh). I'm actually starting to be able to write little scripts in Scala. Here's a quick one that I put together, inspired by Chapter 3 of "Programming Scala" which reads a source code file and formats the file to the screen with line numbers.<br />
<br />
<script src="http://gist.github.com/426501.js">
</script><br />
<br />
I tried to be as functional as possible. The "formatSourceLine" I felt was getting close to the concept, using recursion, immutable values, and always returning a value (avoiding the side effect of printing from within the function).<br />
<br />
The actual output from this script is:<br />
<br />
<pre class="code">D:\Development\Scala>scala file.scala file.scala
1 | import scala.io.Source
2 |
3 | if (args.length > 0) {
4 |
5 | def formatSourcePrefix(line: Int, suffix: String, max: Int = 5):
6 | var prefix: String = ""
7 | for (i <- 1 to (max - line.toString.length))
8 | prefix += " "
9 | prefix + line.toString + suffix
10 | }
11 |
12 | def formatSourceLine(lines: List[String], line: Int = 1,
13 | val out = output + formatSourcePrefix(line, " | ")
14 | if (lines.length > 1)
15 | formatSourceLine(lines.tail, line + 1, out)
16 | else
17 | out
18 | }
19 |
20 | // Read file from disk
21 | val lines = Source.fromPath(args(0)).getLines().toList
22 |
23 | // Print results to screen
24 | println
25 | print(formatSourceLine(lines))
26 |
27 | }
28 | else {
29 | // Print out proper syntax
30 | Console.err.println
31 | Console.err.println("scala file.scala [filename]")
32 | }
</pre>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com1tag:blogger.com,1999:blog-3762802375429612564.post-73842425356325689632010-05-29T11:05:00.002-10:002010-05-29T11:09:53.769-10:00The Difference a Space Can MakeHow can you print the string value of a floating point value of 5 in Scala?<br />
<br />
<pre>scala> 5.toString // Wrong!
res16: java.lang.String = 5
scala> 5. toString
res17: java.lang.String = 5.0
</pre><br />
In the first line scala counts the "." as syntax for "toString method of Integer 5". The second line uses the "toString" method in infix notation, applying the toString method against the floating point value of 5.0.<br />
<br />
Here's another example where a space can make a big difference in type:<br />
<br />
<pre>scala> 4.+(1)
res20: Double = 5.0
scala> 4 .+(1)
res21: Int = 5
</pre>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com1tag:blogger.com,1999:blog-3762802375429612564.post-53085026446845912512010-05-29T09:07:00.000-10:002010-05-29T09:07:50.629-10:00Abstract Types in ScalaWhenever I learn new programming languages, there is invariably features of the language that will surprise me. One of those in Scala (one of many actually) is abstract types. When I first read the textual description of an abstract type, I wondered what use it would ever entail. However, the authors of <a href="http://programming-scala.labs.oreilly.com/"><i>Programming Scala</i></a> gave a very clear example of proper use.<br />
<br />
This code snippet comes from Chapter 2 of the book.<br />
<br />
Starting with an abstract class, we can declare an abstract type. In this case, type "In" on our class "BulkReader" is abstract. It's there, but it does not have a concrete type associated with it. Yet, our value "Source" is typed as type "In". How can this be?<br />
<span class="Apple-style-span" style="font-family: sans-serif;"></span><br />
<pre class="code" style="background-attachment: initial; background-clip: initial; background-color: #f2f2f2; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; font-family: 'andale mono', 'lucida console', monaco, monospace; font-size: 0.875em; margin-bottom: 1em; margin-left: 1em; margin-right: 1em; margin-top: 1em; padding-bottom: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.5em;">abstract class BulkReader {
type In
val source: In
def read: String
}</pre>The magic comes when we declare a concrete version of "BulkReader" and can assign a concrete type to "In":<br />
<span class="Apple-style-span" style="font-family: sans-serif;"></span><br />
<pre class="code" style="background-attachment: initial; background-clip: initial; background-color: #f2f2f2; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; font-family: 'andale mono', 'lucida console', monaco, monospace; font-size: 0.875em; margin-bottom: 1em; margin-left: 1em; margin-right: 1em; margin-top: 1em; padding-bottom: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.5em;">class StringBulkReader(val source: String) extends BulkReader {
type In = String
def read = source
}
class FileBulkReader(val source: File) extends BulkReader {
type In = File
def read = {
val in = new BufferedInputStream(new FileInputStream(source))
val numBytes = in.available()
val bytes = new Array[Byte](numBytes)
in.read(bytes, 0, numBytes)
new String(bytes)
}
}</pre>Very cool! In our two concrete instances of "BulkReader" we assigned two different types to our abstract type "In". Note that the value typed as In, "source" is actually part of the concrete class's constructor which is, at this point for me, completely mind bending in terms of flexibility towards class design.<br />
<br />
I have a feeling it will take me some time to get used to this notion and power before I really start to design code that makes use of it.titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-40942934823137708322010-05-26T08:40:00.000-10:002010-05-26T08:40:50.312-10:00Language of the Summer: Scala?I've been quite fascinated with Haskell as a language. It's purely functional, ridiculously strict typing, and algebraic syntax were completely foreign to me before I began learning about it. I wouldn't say that I <i>learned</i> Haskell, but I did learn about it. I learned enough to know that it's a bit over my head at this point and that I need a better theoretical foundation before I attempt to tackle it again.<br />
<br />
So in the mean time I wanted to take on something more transitional. I had considered F#. It seems like a great choice, especially since I already know C# and am familiar with the .NET platform. But it's a Microsoft language and I was hoping for something more portable (actually, I'm harboring a secret desire to use a functional programming language to one day code for Lego Mindstorms NXT with).<br />
<br />
I happened to stumble upon <a href="http://liftweb.net/">Lift</a> again yesterday. I had heard about it before when I was looking at other web frameworks after watching a keynote from the author of <a href="http://www.seaside.st/">Seaside</a> at <a href="http://djangocon.blip.tv/">Djangocon</a>.<br />
<br />
However, I realized something important about Lift and Scala which I had never caught before: Scala compiles to java bytecode. Java can be run on Google App Engine. I can use Scala/Lift to write for App Engine.<br />
<br />
Suddenly I'm hooked. Oh, awesome, and the book "Programming Scala" is <a href="http://programming-scala.labs.oreilly.com/">available freely online</a>. Mmmm... looks like I have my reading for my traveling this summer.titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-77997162755484909492010-05-25T16:05:00.000-10:002010-05-25T16:05:36.425-10:00Getting a Python/Django JobGetting a job doesn't seem like a fun activity to many people. I'm in the thick of it right now. The problem is when you have a skill set that you really like and that you feel confident with that <i>no company wants</i>. I feel like I'm in that place with Python/Django.<br />
<br />
Sure, I've worked on the "corporaty" systems, ASP.NET, MVC, C#, etc., but what I'd really prefer to work on is python/Django on the web. It doesn't seem like there are <i>any</i> companies hiring for that in Denver (or maybe I need to come up with a better way to find them?). So I end up in an odd place where I wonder if it would be worth a.) going back to the corporaty things that get jobs (MSSQL, ASP, etc.) or b.) learning a newer but similar technology (RoR), and gamble that someone will hire me because I have previous experience in Django.<br />
<br />
No one in Colorado needs Django work done?titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-60275087204770211752010-05-11T12:57:00.001-10:002010-05-11T12:59:01.806-10:00I Bought It at Ross!I was waiting on the wife at Ross Dress for Less the other day, when I happened to notice something: They sell laptop bags! Since when did Ross start carrying those? Surprisingly they had a selection of name brands at, you guessed it, discount prices.<br />
<br />
I ended up buying a <a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&oe=utf-8&client=firefox-a&cid=15912641570362461506&ei=FuDpS_OBPZ7ijASK6bnUDg&sa=title&ved=0CAcQ8wIwADgA#p">Targus Messenger Fusion</a> in dark gray/green for the Toshiba 13.3" I got a few months ago. It was marked down to $18.99 (compared to $26.50 online). It's a pretty nice looking laptop bag with built-in padding and tons of pockets/storage. It has an interesting feature in that it comes with multiple straps so you can keep the green one out for a young/hip look, or put the dark one one for a more serious professional look.<br />
<a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&oe=utf-8&client=firefox-a&cid=15912641570362461506&ei=FuDpS_OBPZ7ijASK6bnUDg&sa=title&ved=0CAcQ8wIwADgA#p"></a><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&oe=utf-8&client=firefox-a&cid=15912641570362461506&ei=FuDpS_OBPZ7ijASK6bnUDg&sa=title&ved=0CAcQ8wIwADgA#p"><img border="0" src="http://base0.googlehosted.com/base_media?q=FroogleCatalog_CNETI657907.jpg&size=20&dhm=f9fbfcfd&hl=en" /></a></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com1tag:blogger.com,1999:blog-3762802375429612564.post-48230312058110313912010-01-14T15:15:00.009-10:002010-01-14T15:44:32.243-10:00Accessing Inherited Models from the Parent in DjangoOne of the neat features of Django's ORM is Model inheritance (table-level). It allows several neat data design patterns to occur. Here's an example. Let's say we're developing a website for a game company. The company sells two types of products: board games and video games. All of the products will share some data in common, name and product_id for example, but we also need to store specific details about each. Using model inheritance we can do something as follows.<br /><pre class="code"><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">Product</span>(models<span style="color: rgb(102, 102, 102);">.</span>Model):<br /> name <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">CharField</span>(max_length<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 160, 112);">75</span>)<br /> product_id <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">SmallIntegerField</span>()<br /> price <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span>DecimalField()<br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">BoardGame</span>(Product):<br /> num_of_players <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">SmallIntegerField</span>()<br /><span style="color: rgb(0, 112, 32);"> game_type</span> <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">CharField</span>(max_length<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 160, 112);">50</span>)<br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">VideoGame</span>(Product):<br /> PLATFORM_CHOICES <span style="color: rgb(102, 102, 102);">=</span> (<br /> (<span style="color: rgb(64, 112, 160);">'wii'</span>, <span style="color: rgb(64, 112, 160);">'Wii),</span><br /> (<span style="color: rgb(64, 112, 160);">'xb3'</span>, <span style="color: rgb(64, 112, 160);">'Xbox 360'</span>),<br /> (<span style="color: rgb(64, 112, 160);">'ps3'</span>, <span style="color: rgb(64, 112, 160);">'Playstation 3'</span>),<br /> )<br /> platform <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">CharField</span>(max_length<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 160, 112);">3</span>, choices<span style="color: rgb(102, 102, 102);">=</span>PLATFORM_CHOICES)<br /></pre>In a real use-case scenario you'd most likely have more than 1 field per, but for this example I wanted to keep things simple.<br /><br />The way Django implements this, if you were to query one of the child models, you'd be able to access the methods from the parent models...<br /><pre class="code">b <span style="color: rgb(102, 102, 102);">=</span> BoardGame<span style="color: rgb(102, 102, 102);">.</span>objects<span style="color: rgb(102, 102, 102);">.</span>all()[<span style="color: rgb(64, 160, 112);">1</span>]<br /><span style="color: rgb(0, 112, 32); font-weight: bold;">print</span> b<span style="color: rgb(102, 102, 102);">.</span>name<br /><br /><span style="color: rgb(102, 102, 102);">>>></span> <span style="color: rgb(64, 112, 160);">'Djangopoly'</span><br /></pre>Another thing that's cool is child instances have a parent instance record. Using the "Djangopoloy" game from above, which is technically type BoardGame, one could still query Product and retrieve it.<br /><pre class="code">p <span style="color: rgb(102, 102, 102);">=</span> Product<span style="color: rgb(102, 102, 102);">.</span>objects<span style="color: rgb(102, 102, 102);">.</span>get(name<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 112, 160);">'Djangopoly'</span>)<br /></pre>This is really useful, but sometimes you need to go the opposite direction, and this is where Django's implementation stops. The link can't go from a Product model instance to a BoardGame. It can't retrieve state as if it was of type BoardGame.<br /><pre><span style="color: rgb(0, 112, 32); font-weight: bold;">print</span> p<span style="color: rgb(102, 102, 102);">.</span>platform<br /><br /><span style="color: rgb(102, 102, 102);">>>></span> CAN<span style="color: rgb(64, 112, 160);">'T DO THAT!</span><br /></pre>Because the need for this seems to be arising more often than not lately for me, I put together a re-usbale bit of code to overcome this limitation. I'll post the code below (a GitHub gist), but using it is actually quite simple.<br /><br />It works by providing an abstract model that the parent model inherits from instead of models.Model:<br /><pre class="code"><span style="color: rgb(0, 112, 32); font-weight: bold;">from</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">inheritance.models</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> ChildAwareModel<br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">Product</span>(ChildAwareModel):<br /><span style="color: rgb(102, 102, 102);">...</span><br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;"> pass</span><br /></pre>Then, an inner class "Inheritance" is supplied to describe children of the model.<br /><pre class="code"><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">Product</span>(ChildAwareModel):<br /> <span style="color: rgb(102, 102, 102);">...</span><br /><br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">Inheritance</span>:<br /> children <span style="color: rgb(102, 102, 102);">=</span> (<br /> <span style="color: rgb(64, 112, 160);">'myapp.models.BoardGame'</span>,<br /> <span style="color: rgb(64, 112, 160);">'myapp.mdoels.VideoGame'</span>,<br /> )<br /></pre>Only children that need to be reversed to should be set. Once that is configured, a method "get_child_model()" will become available, and can be used like so:<br /><pre class="code">p <span style="color: rgb(102, 102, 102);">=</span> Product<span style="color: rgb(102, 102, 102);">.</span>objects<span style="color: rgb(102, 102, 102);">.</span>get(name<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 112, 160);">'Djangopoly'</span>)<br />b <span style="color: rgb(102, 102, 102);">=</span> p<span style="color: rgb(102, 102, 102);">.</span>get_child_model()<br /><span style="color: rgb(0, 112, 32); font-weight: bold;">print</span> b<span style="color: rgb(102, 102, 102);">.</span>num_of_players<br /><br /><span style="color: rgb(102, 102, 102);">>>></span> <span style="color: rgb(64, 160, 112);">4</span><br /></pre>I'm finding this particularly useful when I've created an aggregate type page -- that is a page that shows a summary of all the generic types (Product) -- but need to on user-click show them some type of product-specific detail.<br /><br />The implementation for ChildAwareModel is below. Save it somewhere on your python path and enjoy. :)<br /><br /><script src="http://gist.github.com/277703.js"></script><br /><a href="http://gist.github.com/277703">ChildAwareModel Gist</a>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com3tag:blogger.com,1999:blog-3762802375429612564.post-67410481318807684882010-01-12T08:11:00.002-10:002010-01-12T08:13:46.712-10:00Version Control Commit MessagesSoooooo... I think I'm going to put in a proposal at work. All commit messages for our Mercurial DVCS need to be in 16-year-old girl language....<br /><br /><ul><li>"Like, the JSON api is totally updated"</li><li>"fixed the buggies kk thx bai <3"</li><li>"this import module is sooooo cuties!!!!!11`1"</li></ul>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-69704229118020757122010-01-10T23:55:00.005-10:002010-01-11T00:04:48.668-10:00Meh @reading (but Yay to reading on the iPod)I can't be the only programmer who finds reading a chore? And what about those 5-billion page programming tomes? Am I the only one who never finishes those?<div><br /></div><div>It happens to be that I just found something about reading that totally rocks: Reading on the iPhone/iPod Touch is totally different (better different). HA! I bet you didn't know I was going to say that (well if you read the title of this post you probably did.. that was totally a spoiler).</div><div><br /></div><div>Now it seems that I am finding I actually enjoy reading again (did I just write that?). I ended up landing a very fetching iPod touch for Christmas which has been having it's battery drained near non-stop since the package was opened. As a curiosity of sorts, I threw a lot of the big name apps onto it, including Kindle for Ipod, and purchased a book I had been eying for a while, <a href="http://www.amazon.com/JavaScript-The-Good-Parts-ebook/dp/B0026OR2ZY">Javascript: The Good Parts</a> (Douglas Crockford), to try out the experience of reading on the iPod.</div><div><br /></div><div>It seems so wrong but I found I actually <i>prefer</i> reading on the iPod. I've read a number of paper books, and I've tried reading eBook/PDFs on the computer, but neither of them really jump out at me. But the iPod... the iPod I like.</div><div><br /></div><div>The reason for this, I suspect, is that reading on the iPod is not intimidating. The screen only shows 2 paragraphs at a time. I don't need to be worried about the rest of the book, I'm only looking at these 2 paragraphs. What I think this does for me is it allows me to read the book without feeling like I have to rush to get to the end.</div><div><br /></div><div>My wife thinks it's silly, but <b><i>whatever</i></b> -- I'm already on my 2nd book.... <a href="http://www.amazon.com/Real-World-Haskell-ebook/dp/B0026OR2FY">Real World Haskell</a> (Donald Stewart). Oooooh yes, it's <a href="http://www.haskell.org/">Haskell</a> time! Expect some really weirded out blog posts from me in the future about functional programming.</div><div><br /></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-53193893388879748572009-12-18T16:16:00.010-10:002009-12-18T16:36:16.619-10:00Where Have All The Good DatePickers Gone?Is it just me, or is it hard to find a good jQuery-based date picker? Or maybe I'm just picky.<br /><br />The widget needs to support selecting a date range (there goes most of them). And it needs to be able to support configuring a starting and ending time. And it has to support Themeroller (there goes whatever few remained).<br /><br />So as it seems is starting to become a norm for me, I'm working on a custom jQuery widget. It's been provisionally named "LoneRangeSelector". It's for a custom web-app-calendar sort of thing.<br /><br />Provisionally, here's my progress thus far...<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilttrtqv553lA5iuZPvyRGwYubXvKehN9PchrNK_tCXZ6nSaO5ubkao2NWa9XuZG8z-myno8gxfi95BbrZo08Q_G1VddWANcm6e8FgIrVPlGVUzxRiDywuPspvrJUcazSTyFQ1BqNMEGk/s1600-h/LoneRangeSelector.png"><img style="cursor: pointer; width: 400px; height: 177px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilttrtqv553lA5iuZPvyRGwYubXvKehN9PchrNK_tCXZ6nSaO5ubkao2NWa9XuZG8z-myno8gxfi95BbrZo08Q_G1VddWANcm6e8FgIrVPlGVUzxRiDywuPspvrJUcazSTyFQ1BqNMEGk/s400/LoneRangeSelector.png" alt="" id="BLOGGER_PHOTO_ID_5416768666569823938" border="0" /></a><br /><br />The main goal is to make an interface that's quick and easy to setup the date-related parameters of an event, without having to enter 4 different fields (start date, start time, end date, end time).<br /><br />The secondary goal is that as a byproduct of having a single widget, there is quite a bit less error checking that needs to be done (ie. it's impossible to type in an ending date that occurs before the starting date).<br /><br />I actually re-created the calendar code instead of trying to modify jQuery UI's datepicker, though it does implement identical css classes so the Themeroller appearance is the same.<br /><br />The lower portion has been one of the more challenging. I think the time slider needs to have some type of time indication on it, because just at first glance there's no way you konw that it spans midnight to midnight.<br /><br />Concept:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidJ3mxbjJTWe1PyKion6vsIGBgnQb2qAUC1cUZFNhLqY-GdBlmEwyTSakAEF0OKYlOuGzhgqiheF1el5fEhyphenhyphenzjh4mkYDHgeVHG3Jf_qvMGKf3xQyBqv8Pn-xHdtRBQxjMeTukphIqIWMM/s1600-h/LoneRangeSelector-ticks.png"><img style="cursor: pointer; width: 400px; height: 203px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidJ3mxbjJTWe1PyKion6vsIGBgnQb2qAUC1cUZFNhLqY-GdBlmEwyTSakAEF0OKYlOuGzhgqiheF1el5fEhyphenhyphenzjh4mkYDHgeVHG3Jf_qvMGKf3xQyBqv8Pn-xHdtRBQxjMeTukphIqIWMM/s400/LoneRangeSelector-ticks.png" alt="" id="BLOGGER_PHOTO_ID_5416770520153510082" border="0" /></a>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-53757021250288385832009-12-11T09:31:00.003-10:002009-12-11T09:45:34.411-10:00Using Google's jQuery CDN with Django when not in developmentI wanted to transition the Django site I work with primarily to use <a href="http://code.google.com/apis/ajaxlibs/documentation/">Google's jQuery CDN</a>. However, when developing locally, it's often faster to just use a local copy. What I wanted was a way to toggle which copy of jQuery was being used based on the environment.<br /><br /><span style="font-weight: bold;font-size:130%;" >Environment Detection</span><br />Before we can toggle the jQuery location, we need to have a way to detect which environment we're running in.I <a href="http://urlencode.blogspot.com/2009/10/notating-environment-in-djangos.html">previously blogged about</a> how I have my settings.py configured. There are different ways to do this. One is to use local_settings.py, the other is to have conditional code in your main settings.py which determines values. Either way works.<br /><br />As for my setting, on the webserver is a folder structure that resembles...<br /><pre> \webroot<br /> \django_devel<br /> \django_prod<br /></pre>My webserver pulls double duty, hosting both a production version ("prod") and a staging/testing version ("devel"). In my settings.py file I'm using this to determine which environment Django is running in...<br /><pre><span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">os</span><br />DJANGO_ROOT <span style="color: rgb(102, 102, 102);">=</span> os<span style="color: rgb(102, 102, 102);">.</span>path<span style="color: rgb(102, 102, 102);">.</span>abspath(os<span style="color: rgb(102, 102, 102);">.</span>path<span style="color: rgb(102, 102, 102);">.</span>dirname(__file__))<br /><br /><span style="color: rgb(96, 160, 176); font-style: italic;">#</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># Globals for determining settings</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;">#</span><br />STAGING <span style="color: rgb(102, 102, 102);">=</span> PRODUCTION <span style="color: rgb(102, 102, 102);">=</span> DEVELOPMENT <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(0, 112, 32);">False</span><br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">if</span> <span style="color: rgb(64, 112, 160);">'django_devel'</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">in</span> DJANGO_ROOT:<br />STAGING <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(0, 112, 32);">True</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">elif</span> <span style="color: rgb(64, 112, 160);">'django_prod'</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">in</span> DJANGO_ROOT:<br />PRODUCTION <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(0, 112, 32);">True</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">else</span>:<br />DEVELOPMENT <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(0, 112, 32);">True</span><br /></pre><br />Most likely, you'll have some other what you're determining your environment. In that case just substitute that.<br /><br /><span style="font-weight: bold;font-size:130%;" >{% jquery template tag %}</span><br />Now that we have the required support code to detect what environment we're running in, putting together the actual implementation is simple.<br /><br />I decided to go with a template tag. It's simple and easy.<br /><pre><span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">settings</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">from</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">django</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> template<br /><span style="color: rgb(0, 112, 32); font-weight: bold;">from</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">django.conf</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> settings<br /><br />register <span style="color: rgb(102, 102, 102);">=</span> template<span style="color: rgb(102, 102, 102);">.</span>Library()<br /><br /><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># -----------------------------------------------------------------------------</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># jQuery</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># -----------------------------------------------------------------------------</span><br /><span style="color: rgb(85, 85, 85); font-weight: bold;">@register</span><span style="color: rgb(102, 102, 102);">.</span>tag<br /><span style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span style="color: rgb(6, 40, 126);">jquery</span>(parser, token):<br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">return</span> JQueryNode()<br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">JQueryNode</span>(template<span style="color: rgb(102, 102, 102);">.</span>Node):<br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span style="color: rgb(6, 40, 126);">render</span>(<span style="color: rgb(0, 112, 32);">self</span>, context):<br /> jquery <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(64, 112, 160);">'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'</span><br /> jquery_ui <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(64, 112, 160);">'http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js'</span><br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">if</span> <span style="color: rgb(0, 112, 32);">getattr</span>(settings, <span style="color: rgb(64, 112, 160);">'DEVELOPMENT'</span>, <span style="color: rgb(0, 112, 32);">True</span>):<br /> media_url <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(0, 112, 32);">getattr</span>(settings, <span style="color: rgb(64, 112, 160);">'MEDIA_URL'</span>, <span style="color: rgb(64, 112, 160);">'/media/'</span>)<br /> jquery <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(64, 112, 160);">'</span><span style="color: rgb(112, 160, 208); font-style: italic;">%s</span><span style="color: rgb(64, 112, 160);">js/jquery-1.3.2.min.js'</span> <span style="color: rgb(102, 102, 102);">%</span> media_url<br /> jquery_ui <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(64, 112, 160);">'</span><span style="color: rgb(112, 160, 208); font-style: italic;">%s</span><span style="color: rgb(64, 112, 160);">js/jquery-ui-1.7.2.custom.min.js'</span> <span style="color: rgb(102, 102, 102);">%</span> media_url<br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">return</span> <span style="color: rgb(64, 112, 160);">'<script type="text/javascript" src="</span><span style="color: rgb(112, 160, 208); font-style: italic;">%s</span><span style="color: rgb(64, 112, 160);">"></script><script type="text/javascript" src="</span><span style="color: rgb(112, 160, 208); font-style: italic;">%s</span><span style="color: rgb(64, 112, 160);">"></script>'</span> <span style="color: rgb(102, 102, 102);">%</span> (jquery, jquery_ui)<br /></pre><br />There are a couple of things to note here.<br /><br />First, there's a hard-coded path in this code. Because I plan to always use this template tag every time I need jQuery, I felt confident doing so as it leaves only one place to edit. However, if you won't be following that rigid of an implementation pulling the locations of jQuery out and into a settings.py file would probably be a smart idea.<br /><br />Second, I'm implementing my type of detection for the environment. This probably differs from the majority of Django users.<br /><br />When all is said and done, all that needs to remain is to simply drop the tag into base.html and we're good to go.<br /><pre><span style="color: rgb(0, 112, 32);">{%</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">load</span> <span style="color: rgb(187, 96, 213);">jquery</span> <span style="color: rgb(0, 112, 32);">%}{%</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">jquery</span> <span style="color: rgb(0, 112, 32);">%}</span><br /></pre>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com1tag:blogger.com,1999:blog-3762802375429612564.post-72314139788259883342009-12-10T09:20:00.007-10:002009-12-10T09:32:42.299-10:00How can I create a page that regularly updates itself with Django?It seems this question has been coming up a lot on Stack Overflow, and I wrote a pretty lengthy response which I'll include here. The original question is <a href="http://stackoverflow.com/questions/1879872/django-update-div-with-ajax/1883266">#1883266</a>.<br /><br /><span style="font-weight: bold;">Q: I'm building a web application that needs to have content on the page updated in real time without a page refresh (like a chat or messaging application). How do I do this with Django? In other words, how do I use AJAX with Django?</span><br /><br />A: There's a lot going on in order to make this process work... <ul><li>The client regularly polls the server for new chat entries</li><li>The server checks for and only replies with the newest </li><li>The client receives the newest entries and appends them to the DOM</li></ul> <p>This can be confusing when you're first starting because it's not always clear what the client does and what the server does, but if the large problem is broken down I think you'll find it's a simple process.</p> <p>If the client is going to regularly poll the server for new chat entries, then the server (django) needs to have some type of API to do so. Your biggest decision will be what data type the server returns. You can choose from: rendered HTML, XML, YAML, or JSON. The lightest weight is JSON, and it's supported by most of the major javascript frameworks (and django includes a JSON serializer since it's that awesome).<br /></p><pre><span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"># (models.py)</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># Your model I'm assuming is something to the effect of...</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">class</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">ChatLine</span>(models<span style="color: rgb(102, 102, 102);">.</span>Model):<br /> screenname <span style="color: rgb(102, 102, 102);">=</span> model<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">CharField</span>(max_length<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 160, 112);">40</span>)<br /> value <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">CharField</span>(max_length<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 160, 112);">100</span>)<br /> created <span style="color: rgb(102, 102, 102);">=</span> models<span style="color: rgb(102, 102, 102);">.</span><span style="color: rgb(0, 112, 32);">DateTimeField</span>(default<span style="color: rgb(102, 102, 102);">=</span>datetime<span style="color: rgb(102, 102, 102);">.</span>now())<br /><br /><span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"># (urls.py)</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># A url pattern to match our API...</span><br />url(<span style="color: rgb(64, 112, 160);">r'^api/latest-chat/(?P<seconds_old>\d+)/$'</span>,get_latest_chat),<br /><br /><span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"># (views.py)</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"># A view to answer that URL</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span style="color: rgb(6, 40, 126);">get_latest_chat</span>(request, seconds_old):<br /><span style="color: rgb(96, 160, 176); font-style: italic;"> # Query comments since the past X seconds</span><br /> chat_since <span style="color: rgb(102, 102, 102);">=</span> datetime<span style="color: rgb(102, 102, 102);">.</span>datetime<span style="color: rgb(102, 102, 102);">.</span>now() <span style="color: rgb(102, 102, 102);">-</span> datetime<span style="color: rgb(102, 102, 102);">.</span>timedelta(seconds<span style="color: rgb(102, 102, 102);">=</span>seconds_old)<br /> chat <span style="color: rgb(102, 102, 102);">=</span> Chat<span style="color: rgb(102, 102, 102);">.</span>objects<span style="color: rgb(102, 102, 102);">.</span>filter(created__gte<span style="color: rgb(102, 102, 102);">=</span>comments_since)<br /><br /><span style="color: rgb(96, 160, 176); font-style: italic;"> # Return serialized data or whatever you're doing with it</span><br /><span style="color: rgb(0, 112, 32); font-weight: bold;"> return</span> HttpResponse(simplejson<span style="color: rgb(102, 102, 102);">.</span>dumps(chat),mimetype<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(64, 112, 160);">'application/json'</span>)<br /></pre><br />So whenever we poll our API, we should get back something like this (JSON format)...<br /><pre><span style="color: rgb(102, 102, 102);">[</span><br /> {<br /> <span style="color: rgb(64, 112, 160);">'value'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'Hello World'</span><span style="color: rgb(102, 102, 102);">,</span><br /> <span style="color: rgb(64, 112, 160);">'created'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'2009-12-10 14:56:11'</span><span style="color: rgb(102, 102, 102);">,</span><br /> <span style="color: rgb(64, 112, 160);">'screenname'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'tstone'</span><br /> }<span style="color: rgb(102, 102, 102);">,</span><br /> {<br /> <span style="color: rgb(64, 112, 160);">'value'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'And more cool Django-ness'</span><span style="color: rgb(102, 102, 102);">,</span><br /> <span style="color: rgb(64, 112, 160);">'created'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'2009-12-10 14:58:49'</span><span style="color: rgb(102, 102, 102);">,</span><br /> <span style="color: rgb(64, 112, 160);">'screenname'</span><span style="color: rgb(102, 102, 102);">:</span><span style="color: rgb(64, 112, 160);">'leethax0r1337'</span><br /> }<span style="color: rgb(102, 102, 102);">,</span><br /><span style="color: rgb(102, 102, 102);">]</span><br /></pre><br />On our actual page, we have a <code><div></code> tag which we'll call <code><div id="chatbox"></code> which will hold whatever the incoming chat messages are. Our javascript simple needs to poll the server API that we created, check if there is a response, and then if there are items, append them to the chat box.<br /><br /><pre><span style="color: rgb(96, 160, 176); font-style: italic;"><!-- I'm assuming you're using jQuery --></span><br /><span style="color: rgb(6, 40, 115); font-weight: bold;"><script </span><span style="color: rgb(64, 112, 160);">type="text/javascript"</span><span style="color: rgb(6, 40, 115); font-weight: bold;">></span><br /><br /> <span style="color: rgb(102, 102, 102);">LATEST</span>_CHAT_URL <span style="color: rgb(102, 102, 102);">=</span> <span style="color: rgb(64, 112, 160);">'</span><span style="color: rgb(0, 112, 32);">{%</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">url</span> <span style="color: rgb(187, 96, 213);">get_latest_chat</span> <span style="color: rgb(64, 160, 112);">5</span> <span style="color: rgb(0, 112, 32);">%}</span><span style="color: rgb(64, 112, 160);">'</span><span style="color: rgb(102, 102, 102);">;</span><br /><br /> <span style="color: rgb(96, 160, 176); font-style: italic;">// On page start...</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"></span> $(<span style="color: rgb(0, 112, 32); font-weight: bold;">function</span>() {<br /> <span style="color: rgb(96, 160, 176); font-style: italic;">// Start a timer that will call our API at regular intervals</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"></span> <span style="color: rgb(96, 160, 176); font-style: italic;">// The 2nd value is the time in milliseconds, so 5000 = 5 seconds</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"></span> setTimeout(updateChat<span style="color: rgb(102, 102, 102);">,</span> <span style="color: rgb(64, 160, 112);">5000</span>)<br /> });<br /><br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">function</span> updateChat() {<br /> $.getJSON(<span style="color: rgb(102, 102, 102);">LATEST</span>_CHAT_URL<span style="color: rgb(102, 102, 102);">,</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">function</span>(data){<br /> <span style="color: rgb(96, 160, 176); font-style: italic;">// Enumerate JSON objects</span><br /><span style="color: rgb(96, 160, 176); font-style: italic;"></span> $.each(data.items<span style="color: rgb(102, 102, 102);">,</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">function</span>(i<span style="color: rgb(102, 102, 102);">,</span>item){<br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">var</span> newChatLine <span style="color: rgb(102, 102, 102);">=</span> $(<span style="color: rgb(64, 112, 160);">'<span class="chat"></span>'</span>);<br /> newChatLine.append(<span style="color: rgb(64, 112, 160);">'<span class="user">'</span> <span style="color: rgb(102, 102, 102);">+</span> item.screenname <span style="color: rgb(102, 102, 102);">+</span> <span style="color: rgb(64, 112, 160);">'</span>'</span>);<br /> newChatLine.append(<span style="color: rgb(64, 112, 160);">'<span class="text">'</span> <span style="color: rgb(102, 102, 102);">+</span> item.text <span style="color: rgb(102, 102, 102);">+</span> <span style="color: rgb(64, 112, 160);">'</span>'</span>);<br /> $(<span style="color: rgb(64, 112, 160);">'#chatbox'</span>).append(newChatLine);<br /> });<br /> });<br /> }<br /><br /><span style="color: rgb(6, 40, 115); font-weight: bold;"></script></span><br /><br /><span style="color: rgb(6, 40, 115); font-weight: bold;"><div</span> <span style="color: rgb(64, 112, 160);">id="chatbox"</span><span style="color: rgb(6, 40, 115); font-weight: bold;">></span><br /><span style="color: rgb(6, 40, 115); font-weight: bold;"></div></span><br /></pre>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-59099183674629249462009-12-08T11:02:00.005-10:002009-12-08T11:39:40.213-10:00Efficiently View Apache Log Files on WindowsOne of the annoying things about using multiple platforms is when one platform has a useful utility (no matter how small) and the other platform doesn't. Have you ever needed to regularly check an Apache log file on your Windows development machine? The shell user inside me says "just tail it"... but this is Windows.<br /><br />However, I just found a really amazing tool called <a href="http://www.baremetalsoft.com/baretail/">BareTail</a>.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVqkkn-yY70_DI2thKES60an6gHLspuKyCd94UKEuWRnxa_MRjf4yt4ynABoVu37545-NBHlyoRq8UbMK5fTKUzMFfdJ4-PgslI-xQ5jdoEf7R_G8hC0Z6IBeQ8YskuvaMKahyphenhyphenAZrnHdw/s1600-h/untitled.JPG"><img style="cursor: pointer; width: 400px; height: 165px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVqkkn-yY70_DI2thKES60an6gHLspuKyCd94UKEuWRnxa_MRjf4yt4ynABoVu37545-NBHlyoRq8UbMK5fTKUzMFfdJ4-PgslI-xQ5jdoEf7R_G8hC0Z6IBeQ8YskuvaMKahyphenhyphenAZrnHdw/s400/untitled.JPG" alt="" id="BLOGGER_PHOTO_ID_5412982611066610802" border="0" /></a><br /><br />BareTail "connects" to a log file, and shows you an automatically updated (live) tail of that file. It basically allows this situation to happen:<br /><br />I have a window up on my developer machine (in the 2nd monitor) which is the tail of the Apache error log. Every time something is written to that log, BareTail pushes it into the window on my screen. I see log entries in real time.<br /><br />Let me tell you, it makes debugging Apache error log issues much more efficient.titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-42490995479867322072009-12-08T08:42:00.003-10:002009-12-08T08:53:22.275-10:00Time for Python Neenjah!I happened upon an administrative assistant today who was renaming files using Windows Explorer. She had a folder with about 50 or so sub-folders, many of which contained sub-folders of their own. Every file needed to be renamed with the company name prefixing it. So for example, the file "january-charts.pdf" needed to be renamed to "Company - january-charts.pdf".<br /><br />"You know", I said, "I could help that go a little faster if you're interested." I happened to know that these files needed to be sent as of yesterday, so I figured she wouldn't mind me helping to trim an hour or two off them getting out. That must mean it's time for <span style="font-weight: bold;">Python Neenjah</span>! (In case you missed it, that was a somewhat veiled reference to <a href="http://xkcd.com/208/">xkcd 'Regular Expressions'</a>)<br /><br />A month or two ago I had put together a re-usable python module to allow easy recursively searching a directory. I know, I know, python already includes similar functionality. But it was weird to me, and I wanted a simpler and more flexible format. The module I built, inspire by some ideas I found around the internet, allows a callback to be specified whenever a file is found. It means you can do virtually anything from that directory search, and never really be concerned about how it does it.<br /><br /><pre><span style="color: rgb(0, 112, 32); font-weight: bold;">from</span> <span style="color: rgb(14, 132, 181); font-weight: bold;">dirsearch</span> <span style="color: rgb(0, 112, 32); font-weight: bold;">import</span> DirSearch<br /><br /><span style="color: rgb(0, 112, 32); font-weight: bold;">def</span> <span style="color: rgb(6, 40, 126);">search_callback</span>(<span style="color: rgb(0, 112, 32);">file</span>):<br /> <span style="color: rgb(0, 112, 32); font-weight: bold;">print</span> <span style="color: rgb(0, 112, 32);">file</span><br /><br /><span style="color: rgb(0, 112, 32);">dir</span> <span style="color: rgb(102, 102, 102);">=</span> DirSearch(<span style="color: rgb(64, 112, 160);">'C:\Path\Whatever'</span>, show_output<span style="color: rgb(102, 102, 102);">=</span><span style="color: rgb(0, 112, 32);">True</span>)<br /><span style="color: rgb(0, 112, 32);">dir</span><span style="color: rgb(102, 102, 102);">.</span>search(search_callback)<br /></pre><br />The source for dirsearch.py is included below.<br /><br />Well with my module it only took a few lines of code to put together a command to complete the task at hand.<br /><br /><a href="http://gist.github.com/251883">DirSearch.py</a><br /><script src="http://gist.github.com/251883.js"></script><br /><br /><a href="http://gist.github.com/251885">rrename.py</a><br /><script src="http://gist.github.com/251885.js"></script>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-86079500632760315832009-12-04T09:10:00.002-10:002009-12-04T09:14:06.308-10:00MarkEdit in DjangoBeing primarily a Django developer, it was only a matter of time until MarkEdit came with Django integration.<br /><br />I added to the GitHub repo today a simple app which provides a widget that renders MarkEdit through Django. It's probably a bit more difficult to use than the jQuery plugin because it makes some assumptions about your Django configuration and usage (as do many Django apps). In any case, an initiated Django developer should be able to figure it out just fine.<br /><br />In addition to the source update, the wiki has also been updated with documentation for the <a href="http://wiki.github.com/tstone/jquery-markedit/django-integration">Django integration</a>.<br /><br />An of course, here's a screenshot of MarkEdit in the admin (running under <a href="http://code.google.com/p/django-grappelli/">Grappelli</a>):<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAZUPQU5cjYdizo6bSuDJ8ImDaLYKqqybKzxdjHrvZ-ZFfzGXZl_VfsZdD-OehUCUhMc0hUwsbSMQHguP2Drf7KGajpW7QjEyWArMMBhu-1DfRUlD3EeBFfff11zYsjR0zt3eTTf30eZE/s1600-h/markedit-admin.jpg"><img style="cursor: pointer; width: 400px; height: 363px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAZUPQU5cjYdizo6bSuDJ8ImDaLYKqqybKzxdjHrvZ-ZFfzGXZl_VfsZdD-OehUCUhMc0hUwsbSMQHguP2Drf7KGajpW7QjEyWArMMBhu-1DfRUlD3EeBFfff11zYsjR0zt3eTTf30eZE/s400/markedit-admin.jpg" alt="" id="BLOGGER_PHOTO_ID_5411461352632900834" border="0" /></a>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-71599275106817610122009-12-03T13:19:00.004-10:002009-12-04T00:11:40.259-10:00MarkEdit Moved to GitHubI have relocated the <a href="http://github.com/tstone/jquery-markedit/">MarkEdit repository to GitHub</a> to give it more community accessibility. The GitHub repo now also includes an <a href="http://wiki.github.com/tstone/jquery-markedit">improved wiki</a>.<div><br /></div><div>MarkEdit now has it's own project page, also hosted by GitHub: <a href="http://tstone.github.com/jquery-markedit/">http://tstone.github.com/jquery-markedit/</a></div><div><br /></div><div>The project page <i>finally</i> puts a <b>working demo</b> online (along with 4 other advanced demos, showing some of MarkEdit's flexibility).</div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-68096841817792217682009-12-02T23:02:00.003-10:002009-12-02T23:32:48.832-10:00IE TextRanges, Selections, and Carriage ReturnsI spent several hours this week trying to track down a bug with programmatically selecting text via javascript.<div><br /></div><pre class="code"><span style=" font-weight: bold;color:#007020;">if</span> (textarea.setSelectionRange) {<br /> <span style=" font-style: italic;color:#60a0b0;">// Set selection for Mozilla-ish browsers</span><br /><span style=" font-style: italic;color:#60a0b0;"></span> textarea.setSelectionRange(start<span style="color:#666666;">,</span> end);<br /> }<br /> <span style=" font-weight: bold;color:#007020;">else</span> {<br /> <span style=" font-style: italic;color:#60a0b0;">// Set selection for IE</span><br /><span style=" font-style: italic;color:#60a0b0;"></span> <span style=" font-weight: bold;color:#007020;">var</span> range <span style="color:#666666;">=</span> textarea.createTextRange();<br /> range.collapse(<span style=" font-weight: bold;color:#007020;">true</span>);<br /> range.moveEnd(<span style="color:#4070a0;">'character'</span><span style="color:#666666;">,</span> start);<br /> range.moveStart(<span style="color:#4070a0;">'character'</span><span style="color:#666666;">,</span> end);<br /> range.select();<br />}<br /></pre><div>Assuming that 'textarea' is the element of the textarea, and 'start' and 'end' are Number values that indicate the start and end of the selection, respectively.</div><div><br /></div><div>I was managing this in MarkEdit by keeping a state object that recorded the "Before Select" (everything before the selection), the selection text, and the "After Select" (everything after the selection).</div><div><br /></div><div>So it seems easy to implement right?</div><div><pre>start <span style="color:#666666;">=</span> beforeSelect.length<span style="color:#666666;">;</span><br />end <span style="color:#666666;">=</span> start <span style="color:#666666;">+</span> select.length<span style="color:#666666;">;</span><br /></pre><b>That doesn't always work!</b></div><div><b><br /></b></div><div>It works sometimes, but occasionally the selection will be the proper length, but randomly offset by 3-7 characters. It took quite a bit to figure out (that's an understatement). Here's what a lot of debugging and experimentation came up with:</div><div><br /></div><div>When Internet Explorer creates a Range or a TextRange object, it converts all line returns to Carriage Return/Line Feed format; basically [0A] -> [0D 0A]. Easy to counter? Just determine all the instances of [0D] and [0A], subtract the two, then you have the offset? That's what I thought too, but even with the offset, the discrepancy persisted.</div><div><br /></div><div>So it was time for a new approach. What if we converted all of the [0A] characters in the textarea to [0D 0A]'s ? Again, the offset of the selection persisted.</div><div><br /></div><div>Here's what I finally narrowed it down to be: When you create a TextRange, IE doesn't actually re-render the text internally, converting all [0A]'s. However, when you request the text (TextRange.text) it converts it at that time. This means if you were to get the length of the selected text, TextRange.text.length, it would be longer than what the TextRange is seeing internally! When using .moveEnd on the TextRange, the value counts the position from the internal text, not from the text it gives you.</div><div><br /></div><div>That is reeeeeeediculously wrong. The final solution is to "sanitize" the text TextRange gives you by removing all [0D] characters from it. From there you can accurately calculate the selection position and give the TextRange the proper coordinates.</div><div><br /></div><div>Whew... that was several hours needlessly wasted by IE (again).</div><div><br /></div><div><br /></div><div><br /></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-63426919601283691262009-11-30T08:57:00.002-10:002009-11-30T08:58:38.503-10:00MarkEdit DocumentationFor those interested, I finished the preliminary documentation and sample code for <a href="http://bitbucket.org/tstone/jquery-markedit/">MarkEdit</a>. It can be viewed via the <a href="http://bitbucket.org/tstone/jquery-markedit/wiki/Home">Bitbucket Wiki</a>.<br /><br />The documentation includes...<br /><ul><li>Installation</li><li>Sample code</li><li>Complete API reference</li><li>Complete configuration option reference</li></ul>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com3tag:blogger.com,1999:blog-3762802375429612564.post-32987369616217024462009-11-28T09:38:00.002-10:002009-11-28T10:08:27.557-10:00Developer Blog EntriesI haven't done any development in the past few days thanks to this holiday weekend, however I did have a chance to catch up on my blog subscriptions (which were really beginning to pile up). <i>My wife just peeked over my should and said I was also spending quality time with her.</i><div><ul><li><a href="http://tomayko.com/writings/unicorn-is-unix">Ryan Tomayko on Unicorn</a> (ruby), "because it's Unix." A neat post on using some of the unixy functionalities of ruby to develop a network service. I'd really like to try it, but I don't have a POSIX computer up and running. Hmmm... I should fix that.<br /> </li><li><a href="http://simonwillison.net/2009/Nov/23/node/">Simon Willison talk</a>s about <a href="http://nodejs.org/">Node.js</a>, a server-side javascript framework which looks ridiculously cool. Having been spending a lot of time working on MarkEdit, I can certainly appreciate some of the non-blocking/callback ideas that it implements. However, again with the lack of POSIX. It doesn't yet run on Windows. I really need to remedy that.<br /> </li><li>Again from Simon Willison he mentions <a href="http://flxhr.flensed.com/">flXHR</a> -- A replacement for XmlHttpRequest, but implemented by an invisible flash (.swf) wrapper layer. It's a really interesting concept, though completely hacky. Why bother with this? 1.) get around cross-browser issues, and 2.) get around browser security issues (like making an AJAX request to API on another domain for example).<br /></li></ul></div>titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0tag:blogger.com,1999:blog-3762802375429612564.post-74487023342677637212009-11-23T16:55:00.008-10:002009-11-24T09:49:48.784-10:00WMD Editor & jQueryI've been using Dana Robinson's WMD Editor fork for a couple of months now. It was a great place to start. The problem is, it's becoming a real pain to customize. Small things were taking huge amounts of time to accomplish so I had a tough decision to make: Continue to use WMD Editor and have to spend hours for customization that should only take a minute or two -OR- re-write the UI myself on top of the existing Markdown rendering component?<br /><br />Introducing: <a style="font-weight: bold;" href="http://bitbucket.org/tstone/jquery-markedit">MarkEdit</a> -- the Javascript MarkDown editor built on jQuery (to replace WMD Editor). It's currently being hosted on <a href="http://bitbucket.org/tstone/jquery-markedit/src/">BitBucket</a>.<br /><ul><li>Built On jQuery/UI -- Lighter weight code and less cross-browser issues </li><li>Themeroller Support -- Use existing or custom jQuery UI Themeroller themes </li><li>International -- Supports the loading of an alternate language </li><li>Sensible Defaults -- You should be able to get up and running with 1 line of javascript </li><li>Configurable -- Almost all defaults can be overridden very easily upon initialization </li><li>Public API -- Interact with whatever part you're interested in </li><li>Documented </li></ul><br />Since everybody loves screenshots, here's the editor running under a jQuery theme and using the Chinese language translation.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLctaVkvSg5ex6T2DV8ELNPbqVCLzch5O7YxaQddmcdwgYX5bjLWODKiUTAn9Jgy-0nRZ9pf42G0d9ncp_H40vY6-FS1xNorY5ZlNw0THeRmhBdyWjdKJIqgw6Iu-g7Cr6wFjp7TVSAqI/s1600/preview1.png"><img style="cursor: pointer; width: 400px; height: 338px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLctaVkvSg5ex6T2DV8ELNPbqVCLzch5O7YxaQddmcdwgYX5bjLWODKiUTAn9Jgy-0nRZ9pf42G0d9ncp_H40vY6-FS1xNorY5ZlNw0THeRmhBdyWjdKJIqgw6Iu-g7Cr6wFjp7TVSAqI/s400/preview1.png" alt="" id="BLOGGER_PHOTO_ID_5407500589288117330" border="0" /></a><br /><br /><br /><br />And again here's a translated popup asking for the URL to insert an image...<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcbiHKtwVvuWT4NvXatnVrAPlEKetpng_LqWf89VK1aF_Ls7DToldsBdeHp9it1zyNAiLcHrSlmD_UKzAtA5hg88HyQe7rNI9xaa9bx-1hooQ5YKhtYYCtpYzV-eMEMADEC-pAblnTKLU/s1600/preview2.png"><img style="cursor: pointer; width: 400px; height: 338px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcbiHKtwVvuWT4NvXatnVrAPlEKetpng_LqWf89VK1aF_Ls7DToldsBdeHp9it1zyNAiLcHrSlmD_UKzAtA5hg88HyQe7rNI9xaa9bx-1hooQ5YKhtYYCtpYzV-eMEMADEC-pAblnTKLU/s400/preview2.png" alt="" id="BLOGGER_PHOTO_ID_5407500951890629058" border="0" /></a><br /><br /><br /><br />And here's preview mode using a different theme but still in Chinese...<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFXK_Cd1SxubWEpsYxETnZfEoH4EmLC7PTZiZAeTZDqL37sVm2UAAeZgWgQRJ_TP2pL7W0Joad3girTQ4Aw6qg5ZfP0xJf6guPRFPByCqjpCNOqCRf81BqyIxJ2w-Bf6SgIzvM_RMe-s0/s1600/preview3.png"><img style="cursor: pointer; width: 400px; height: 338px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFXK_Cd1SxubWEpsYxETnZfEoH4EmLC7PTZiZAeTZDqL37sVm2UAAeZgWgQRJ_TP2pL7W0Joad3girTQ4Aw6qg5ZfP0xJf6guPRFPByCqjpCNOqCRf81BqyIxJ2w-Bf6SgIzvM_RMe-s0/s400/preview3.png" alt="" id="BLOGGER_PHOTO_ID_5407501562768795842" border="0" /></a><br /><br /><br /><br />So far I've invested about 15 hours into the project with it being probably around 95% complete for initial coding. There is still a TON of testing to be done. I haven't even tried to fire it up in any of the fussy browsers yet.<br /><br />I actually wrote a whole lot about it so far -- it's all up on the <a href="http://bitbucket.org/tstone/jquery-markedit/wiki/Home">Wiki</a>. There is still lots more documentation to come.titushttp://www.blogger.com/profile/00219662268164657479noreply@blogger.com0