Wordle
Nov 28 2010 · Random
Discovered wordle on someone’s twitter feed. I love this thing. Here’s the wordle of my post on Enyo:
Nov 28 2010 · Random
Discovered wordle on someone’s twitter feed. I love this thing. Here’s the wordle of my post on Enyo:
Nov 27 2010 · webOS
I missed webOS Developer Day, but I took a look at the presentation of the Enyo framework (video below). I like most of the bullet points: faster performance, hardware acceleration built-in, etc. However, one point that’s troubling me is the idea of using higher-level controls instead of divs. From the bit of code presented in the final demo, it looks like layout (and pretty much all UI stuff) will be done using JavaScript widgets, which are translated at some point into the appropriate HTML constructs. Obviously, widget-centered development isn’t a new concept (MFC, WinForms, Cocoa… sproutcore and such on the web) nor is it necessarily a bad one (getting something presentable up and running is easier), but there always seems to be a very real and very large loss of flexibility.
When a widget doesn’t look or function exactly how you need it to, and it becomes necessary to make a new one, development within the widget framework can range in difficulty from trivial to near-impossible. As I mentioned when I wrote about Adobe Air, HTML/CSS isn’t perfect, but it’s the most flexible layout and styling framework I’ve come across. Abstracting away that flexibility in favor of plug-and-play widgets makes me cringe… it’s a nice idea, it’s a very (object-oriented) developer-ish idea, but it usually comes with a pretty high cost.
From working with both HTML/CSS and WinForms extensively, I’d say the widget-centered framework model used for desktop apps shouldn’t be replicated for web development. In fact, it should be the other way around: the flexibility of a HTML/CSS-esque system should be brought to the desktop.
How this will play out for Enyo, I don’t know. I’m cautiously optimistic. Being a web framework, everything still boils down to HTML and CSS, but it remains to be seen what level of manipulation will be allowed or make sense (in terms of performance, coding difficultly, etc.) at that level.
Nov 21 2010 · webOS
dotspott for webOS v1.3.0 should now be available in the webOS app catalog.
Primarily, there’s a new look compared to the older hotspotdot app. Not a whole lot of new features, but two noteworthy additions:
A smaller update (v1.3.1) should out in the coming week with some minor stylistic additions.
Nov 16 2010 · Web Technologies
I’ve been working on a fairly major update to hotspotdot, both the web client and the webOS mobile app.
First, a new name, accompanied by a new logo.
hotspotdot is now dotspott:
This should (hopefully) put an end to any confusion that the app has anything to do with wi-fi hotspots. It also allows for a much nicer URL as I was able to get dotspott.com (much more elegant compared to the previous, my.hotspotdot.net).
There’s some minor style changes to the index page. Also, some additional code for some minor improves in user experience, such as auto-focus on input fields when a dialog comes up.
The most significant changes are to the main interface. There’s a new look and you’ll notice support for uploading photos. Also, there’s now a 2-column layout; this avoid the issue of having to keep scrolling down the page to show a new location on the map (which was at the top of the page). The map and header areas are fixed (position: fixed), so as you scroll, only the list of locations on the left will move. I debated whether to do this or to contain the list of locations in a div with overflow:auto or overflow:scroll, so the list would have its own scrollbar, and you wouldn’t scroll the entire page to move up and down the list. I have somewhat of a dislike for both solutions, although one was necessary. I opted for the position:fixed approach because manipulating scrollbars within a page is impossible on a touchscreen device (perhaps we’re at the time where we’ll see the end of overflow:auto and overflow:scroll, along with :hover)
The updated webOS app is almost done. I’m just doing some last minute testing before I put it up in the app store, so I’ll have some info and screenshots up soon.
Edit: h/t to Shifting Pixel for the cool Smart Image Resizer; I really wasn’t feeling up to writing image resizing and caching routines, and this little library was a perfect solution.
Nov 7 2010 · Random
I don’t really have a use for it, but Square seems pretty damn cool…
Accept card payments with no contract, monthly fees, or hidden costs. Just plug a free card reader into your mobile device and start swiping.
The transaction fees are reasonable (I think, I’m not really an expert here):
2.75% + 15¢ for swiped transactions
3.5% + 15¢ for keyed-in transactions
There are no activation, gateway, monthly, early termination, or hidden fees.
Nov 5 2010 · javascript
This is the MD5 file hash used in progTools.
You can copy and paste the code below or get it via. bitbucket.
The code builds upon Paul Johnston’s MD5 implementation and you’ll need to include his code first.
<!-- Paul Johnston's MD5 implementation -->
<script type="text/javascript" src="tools/md5/md5.js"></script>
<!-- Additional MD5 functions for working on air.FileStream; requires Paj's md5.js -->
<script type="text/javascript" src="tools/md5/md5_file.js"></script>
core_md5_ex() is a modified/hacked version of the original core_md5() to allow for progressively processing chunks of data, instead of doing it all in one go.
// modified version of Paul Johnston's MD5 implementation
function core_md5_ex(x, len, abcd_start, append_padding, total_len)
{
/* append padding */
if (append_padding) {
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = total_len*8;
}
var a = abcd_start[0];
var b = abcd_start[1];
var c = abcd_start[2];
var d = abcd_start[3];
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
What’s changed is:
hex_md5_stream() is the function which processes the stream: reading in chunks, transforming the bytes in inBytes into an array 4-byte WORDs, and calling core_md5_ex() with the necessary data.
function hex_md5_stream(inStream)
{
var abcd_start = new Array();
abcd_start.push(1732584193);
abcd_start.push(-271733879);
abcd_start.push(-1732584194);
abcd_start.push(271733878);
var inBytes = new air.ByteArray();
var appendPaddingToBlock = false;
var totalLen = 0;
while (inStream.bytesAvailable > 0)
{
inStream.readBytes(inBytes, 0, Math.min(8192, inStream.bytesAvailable));
if(inBytes.length < 8192)
{
appendPaddingToBlock = true;
}
totalLen += inBytes.length;
bin = new Array();
for(var i = 0; i < inBytes.length * 8; i+=8)
bin[i>>5] |= (inBytes[i>>3] & 0xff) << (i%32);
abcd_start = core_md5_ex(bin, inBytes.length*8, abcd_start, appendPaddingToBlock, totalLen);
inBytes.clear();
}
return binl2hex(abcd_start);
}
Here’s the top-level view of how it works:
var inStream = new air.FileStream();
var file = new air.File();
file.url = "file:///" + filename;
inStream.open(file, air.FileMode.READ);
var md5result = hex_md5_stream(inStream);
inStream.close();