Move along, nothing to see here...
Who has time for more than 140 characters these days?
Labels: everything100%off, experiment, failure, goingoutofbusiness, icantbelieveihaventdonethisbefore, me-me-me, moving
Labels: everything100%off, experiment, failure, goingoutofbusiness, icantbelieveihaventdonethisbefore, me-me-me, moving
Labels: experiment, silliness
Starting with a B/W Oscar the Grouch image, and running it through the QArt Coder, got me here http://research.swtch.com/qr/show/e27c9ec04e3013fe - Paint.net took me the rest of the way...
Read about how this works at http://research.swtch.com/qart
Labels: experiment, me-me-me, mobile, silliness, twitter
As a developer, I frequently have to clear my browsers cache, and also cookies, in order to test a site. This is a PITA as now I'm logged out from Google, PivotalTracker, etc, etc.
It also showcases how very few sites store login information in localStorage by default (note to devs, if you're to offer a "Remember Me" button, use localStorage, not a cookie).
So my idea is this: a set of two bookmarklets: the first would retrieve any form data entered in a form (prior to you submitting it) and store that data in localStorage, then the second would fill out a form using the data stored in localStorage for that site.
What about security you might ask? Well, clearly this should only be used on a personal computer - and maybe password fields should be excluded in any case. But this is stored locally, it is not transmitted anywhere, and the data is not accessible to any other site, so the data should stay between you and your computer. One exception would be any potentially malicious script hosted on the site, but that seems like a risk in itself - the same script could much more effectively simply grab the form data on entry.
So - good idea or bad?
Labels: experiment, html5, idea, javascript, random, silliness
Say you have an image, whose width is unknown. You want to display a caption below the image, and the length of the caption text is also unknown. How do you display the caption so that the text wraps to the width of the image?
As far as I know, there is no way to do this with divs and Css. It *may* be possible to do it with figures and figcaptions, but now you’re in Html5 land, and to support older browsers you’ll need to do gymnastics.
You could use an img load event handler and resize the caption after the image comes in, but now you have to add javascript for something that should be handled by your html.
So you use tables. Yes tables, those horrible, horrible remnants of Web 1.0.
And you do it like this (though your styles would obviously be in a css stylesheet somewhere):
<table> <caption style="caption-side: bottom; margin: 0 5px;">Oh caption, my caption! our fearful task is done!<br> The layout has weathered every wrack, the prize we sought is won</caption> <tr><td><img alt="some unknown sized image" src="http://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Ocaptain.jpg/394px-Ocaptain.jpg"> &/lt;tr></table>
which renders thusly (Live writer may corrupt this - sorry view-sourcers):
Also see http://jsfiddle.net/austegard/fGwve/
Labels: css, experiment, howto, html5
Labels: experiment, google, silliness, usability
"“Fun with jQuery Templating and AJAX” by Dan Wellman is a generally interesting article, but I found the code in the “Getting the Data” block especially interesting – see how each get function RETURNS the $.ajax function call, which can then be called inside a when() function, vastly simplifying the workflow (there’s an error in the listed code – getTweets() is supposed to return the ajax function, not simply execute it).
http://net.tutsplus.com/tutorials/javascript-ajax/fun-with-jquery-templating-and-ajax/
Even more interesting is this pattern, suggested by commenter Eric Hynds (whose blog has now been added to my Google Reader list):
$.when( $.get('./tmpl/person.tmpl'), $.getJSON('path/to/data') ) .then(function( template, data ){ $.tmpl( template, data ).appendTo( "#people" ); });
The deferred.done() and then() methods will take as arguments the results from each function called in the when() function – in order – i.e. the output of the get will map to template, and the output from the getJson will be mapped to data. This is pretty sweet!
Perhaps a simpler to observe example of the behavior is shown here: http://jsfiddle.net/austegard/ZaFVg/ - no prize for correct guesses as to the result of this…
/* Hello and World are both treated as resolved deferreds - they can be replaced with any function, like a $.get, etc */ $.when( "Hello", "World" ).then( function(x, y){ alert(x + " " + y); } );
Labels: experiment, howto, javascript, jquery, mashup
Another new browser launch, and another obligatory proves-absolutely-nothing-definite/just-a-single-use-case performance test against the Underscore.js utility framework.
Previous tests showed that IE was gaining on the lead (Chrome), and that is still the case: As seen in the charts below, IE is sometimes faster, but still generally slower than Chrome (longer bars are better):
Again, this test proves very little, other than that IE9’s new Chakra JS engine is still slower than V8 for doing array iterations, and faster for mapping, getting property values, and creating list ranges. IE9 has a number of features Chrome doesn’t have (yet) such as hardware acceleration (the IE Speed reader demo runs 790% faster in IE than in Chrome!) and ES property getter/setter standard compliance, just to mention two random ones…
IE9 beta is a HUGE step forward for IE. Not sure if it will become my default browser, but this is at great day for the web. Now if the EU and other governing bodies can just look the other way while MS quietly replaces all prior IE instances with IE9… ;-)
Labels: experiment, google, javascript, microsoft, rave
After using the heck out of Peldi’s (here and here) and Simon Herd’s (here) original templates for Balsamiq iPad mockups, I decided to give back to the community by creating some templates that I found were missing:
The iPad Portrait and iPad Landscape templates are 1:1 scale iPad mockups, with an inner frame with 1024x768 resolution. I assembled the frame of the iPad piecemeal, so as to leave a “hole” in the middle, for your content to shine through. This is something I sorely missed with Peldi’s templates, forcing me to do far too much work in Paint.Net, manipulating my images to fit his frame exactly.
Complementing these is my iPad Keyboards template. If you’re already using one of the above templates, this one isn’t necessary, but if you want to use Peldi’s templates, my template will come in handy. It's simply the iPad keyboard as Simon Herd originally created it, but in 4 different resolutions, with minor corrections:
Labels: apple, balsamiq, design, experiment, me-me-me
Out of sheer vanity, I added my own blog feed to my Google Reader, as I was curious if anyone ever Liked my posts.
Answer: Nope. :-(
Anyhow, I came across my post on Underscore.js, and since MS just dropped Platform Preview 3 of IE 9, I thought I’d redo the comparison in Chrome6, IE8 and IE9 (though I know this is hardly any complete benchmark test, it’s still telling). The results are below:
As I said in my last post, I can’t wait for IE9 to replace every previous IE version… I haven’t been this excited about an IE product since IE4, which was more than 10 years ago.
IE 8 – still a dog.
IE 9 PP3 – Starting to look good! Faster than Chrome in some tests!
Chrome 6 – still the winner in most categories, though the lead is shrinking
Bottom line, though – if you’re doing a lot of looping/mapping, you should use Underscore rather than jQuery.
Labels: experiment, google, javascript, jquery, me-me-me, microsoft
One of my esteemed colleagues on an internal forum posted about how great IE 9’s HTML5 support was, based on the result of Microsoft’s test pages. MS’s tests are sadly self-selective however: meaning they only seem to test for elements that IE9 supports: http://samples.msdn.microsoft.com/ietestcenter/
W3C Web Standards | Number of Submitted Tests | Internet Explorer 9 Platform Preview | Mozilla Firefox 3.6.3 | Opera 10.52 | Apple Safari 4.05 | Google Chrome 4.1 |
---|---|---|---|---|---|---|
HTML5 | 40 | 78% | 63% | 48% | 43% | 43% |
...” |
Compare that to my own results running html5test.com on the 6(!) browsers I have installed:
Html5 is the first time in a decade that the browser vendors have had a new major standard to fight over; I’m just grateful that this time around we’ll have an army of frameworks such as jQuery that can level the development playing field for us.
Labels: advertising, apple, errors, experiment, firefox, google, html5, microsoft, rant, testing
I came across the very handy-looking Undersore.js today, and clicked on the test & benchmark link. I first ran the test in Chrome. The results below show number of operations per second. Looks like each, map, keys, values, and range are pretty inexpensive operations, whereas uniq and intersect should be used sparingly. All makes sense.
Then out of curiosity, I ran the same tests in IE and Firefox. The exact numbers are not significant as the results vary by 10-20% between subsequent runs in the same browser, but the range is pretty illustrative. And yes, I know IE9 is harder, better, faster, stronger, so this is not a fair fight. I can’t wait for IE9 to replace every previous IE version…
Ops/sec | (higher is | better) | |
Test | Chrome 6 | IE 8 | Firefox 3.6 |
_.each() | 20213 | 510 | 3249 |
_(list).each() | 13570 | 493 | 3161 |
jQuery.each() | 3637 | 209 | 910 |
_.map() | 18581 | 303 | 5488 |
jQuery.map() | 7084 | 686 | 8519 |
_.pluck() | 10852 | 282 | 4785 |
_.uniq() | 127 | 1 | 33 |
_.uniq() (sorted) | 308 | 210 | 84 |
_.sortBy() | 1641 | 45 | 359 |
_.isEqual() | 4962 | 869 | 1826 |
_.keys() | 22675 | 1142 | 4295 |
_.values() | 24551 | 321 | 5435 |
_.intersect() | 83 | 1 | 20 |
_.range() | 33345 | 1223 | 5262 |
Again, why I use Chrome as my default browser.
Labels: experiment, firefox, google, javascript, microsoft, random
Having finished helping Rolling Stone magazine put their archive online (our company, AIS, together with Bondi Digital, did the Silverlight-based archive portion (more on the project later, potentially), another company did the main site), I decided to get artsy by generating a mosaic of the latest issue cover, by using thumbnails of some 1000 previous covers.
AndreaMosaic worked beautifully in creating the mosaic, and even pre-generating the starting point for a Deep Zoom image. I opened that in Deep Zoom Composer, and generated both a Silverlight and an Seadragon Ajax version of the composite. I then uploaded the whole shebang to my public s3 bucket.
It all took a bit of fiddling to get right, but if I had to do it again I could probably do the whole thing in 5-10 minutes… It’s that easy. (Seriously, this blog post is taking longer…)
Of course, now that I’m putting in all the links in this post, I realize I could have simply hosted this at seadragon.com… Oops.
UPDATE:Since MS is so kind to do the processing for me, I figured I might as well create a bigger version of this. The following is around 180MP, made up of 3000 tiles, each 300px tall... Click the full screen button in the embedded viewer for the best effect.
Update, May 12th: ...and here's the latest 2-page cover:
Labels: amazon, experiment, javascript, mashup, me-me-me, silliness, silverlight
For the life of me I can never remember what each property of the Uri class is meant to return. SnippetCompiler to the rescue:
public static void RunSnippet() { Uri foo = new Uri("http://some.domain.com/folder/file.htm?param=val#frag"); foreach(PropertyInfo pi in foo.GetType().GetProperties()){ if (pi.CanRead) { WL("{0}: {1}", pi.Name, pi.GetValue(foo, null)); } } }
Make sure using System.Reflection; and using System.Web; are is included, and there you have it:
AbsolutePath: /folder/file.htm AbsoluteUri: http://some.domain.com/folder/file.htm?param=val#frag Authority: some.domain.com Host: some.domain.com HostNameType: Dns IsDefaultPort: True IsFile: False IsLoopback: False IsUnc: False LocalPath: /folder/file.htm PathAndQuery: /folder/file.htm?param=val Port: 80 Query: ?param=val Fragment: #frag Scheme: http OriginalString: http://some.domain.com/folder/file.htm?param=val#frag DnsSafeHost: some.domain.com IsAbsoluteUri: True Segments: System.String[] UserEscaped: False UserInfo:
UPDATE: Steve Michellotti just showed me how this works beautifully in LinqPad as well, just set the language to C# Statement(s), replace WL with Console.WriteLine and you’re good to go.
Labels: asp.net, asp.net mvc, c#, experiment, howto
<UPDATE date=”2009.12.02”>: WSPBuilder Extensions 2010 Beta 1.3 (or newer) makes the below mostly obsolete – though I was still not able to deploy using the VS add-in – I got a message stating my SPTimer Service was not running.</update>
Based on advice from Bjørn Furuknap (@furuknap, http://furuknap.blogspot.com/2009/11/using-wspbuilder-for-sharepoint-2010.html) I was able to deploy to SP 2010 a rather complex WSPBuilder-based SharePoint solution that I first recompiled in VS 2010.
Steps were:
Labels: experiment, howto, microsoft, programming, sharepoint, SharePoint2010
One of Outlook 2010 (and Gmail)’s better features is that conversations are grouped together – that is: if you send me a message, and I reply, and you reply again, all three messages are grouped together – not just the two you sent me.
The latter is what Outlook 2007 considers a “conversation” – I’d call it a monologue, or at best half a conversation.
To get the full duplex conversation thread in Outlook 2007 do the following:
Enjoy your new duplex view.
Labels: experiment, howto, microsoft, moss
I wasn’t happy with the TagBuilder class, so I improved it… See gist below:
This kind of thing could be useful in MOSS WebPart development as well…
Labels: asp.net, asp.net mvc, c#, experiment, me-me-me, microsoft, moss, mvc, programming
I’m new to github, so therefore I’m also new to gist. At first I couldn’t figure out what they were, but then I watched this great intro video by ByanL which explains it all quite well.
Github seems a bit too command line focused for my taste, but Gist looks useful by itself.
Below I’m going to embed the source for my Twitter Conversations test page, which I copied into my very first Gist – whoa! it loads immediately in the edit panel in Live Writer, nicely color-coded and everything. Nice!
Thanks to Rob Conery for moving Subsonic 3 to github, which made me look in the first place. I’ll be using this.
Labels: experiment, howto, me-me-me, rant, tools
Seth Godin makes some excellent points in his blog post “Graduate school for unemployed college students”. Basically he says unemployed college grads should just approach the next 12 months as if it was another year of school, and spend the time contributing to the community while learning marketable skills. Great concept, but as 3rdgirl and snappers15 point out, this is hard to pull off when faced with student loans or other financial responsibilities. Seth acknowledges this in his followup-post, “Tough!”, but doesn’t offer these people any actual solutions.
How about this for a business idea - and solution to the grad’s financial problem - :
A joint recruiting and student-loan firm that does four things:
Come to think of it, why can’t our colleges do this, already? Or why can’t they provide real-world marketable skills in the first place?
Labels: business, economics, experiment, howto, idea, random
(I'm not sure when I started using the term tokenizer - "formatter" may be more common...)
I’ve experimented in the past with a C# string.Format() alternative that would allow property names as tokens rather than the numeric index based tokens that string.Format() uses. Hardly a unique approach, and many others did it better.
Here’s another first-draft ‘tokenizer’, this time in JavaScript:
String.prototype.format = function(tokens) { ///<summary> ///This is an extension method for strings, using string or numeric tokens (e.g. {foo} or {0}) to format the string. ///<summary> ///<param name="tokens">One or more replacement values ///if a single object is passed, expects to match tokens with object property names, ///if a single string, number or boolean, replaces any and all tokens with the string ///if multiple arguments are passed, replaces numeric tokens with the arguments, in the order passed ///</param> ///<returns>the string with matched tokens replaced</returns> var text = this; try { switch (arguments.length) { case 0: { return this; }; case 1: { switch (typeof tokens) { case "object": { //loop through all the properties in the object and replace tokens matching the names var token; for (token in tokens) { if (!tokens.hasOwnProperty(token) || typeof tokens[token] === 'function') { break; } //else text = text.replace(new RegExp("\\{" + token + "\\}", "gi"), tokens[token]) } return text; }; case "string": case "number": case "boolean": { return text.replace(/{[a-z0-9]*}/gi, tokens.toString()); }; default: return text; }; }; default: { //if multiple parameters, assume numeric tokens, where each number matches the argument position for (var i = 0; i < arguments.length; i++) { text = text.replace(new RegExp("\\{" + i + "\\}", "gi"), arguments[i].toString()); } return text; }; }; } catch (e) { return text; } };
The comment (in VS Intellisense format) is pretty self-explanatory, note that it doesn’t allow any special formatting as String.Format does, nor does it even support escaping {}‘s – in general it is quite crude.
That said, when used within it’s limitations it works - below are a couple of actual usage scenarios, both from my ongoing Twitter Conversations experiment:
var url = "http://search.twitter.com/search.json?q=from:{p1}+to:{p2}+OR+from:{p2}+to:{p1}&since={d1}&until={d2}&rpp=50".format({ p1: $("#p1").attr("value"), p2: $("#p2").attr("value"), d1: $("#d1alt").attr("value"), d2: $("#d2alt").attr("value") });
and
$.getJSON(url + "&callback=?", function(data) { $.each(data.results, function(i, result) { content = ' \ <p> \ <a href="http://twitter.com/{from_user}"><img src="{profile_image_url}" />{from_user}</a> \ (<a href="http://twitter.com/{from_user}/statuses/{id}">{created_at}</a>): \ {text} \ </p>'.format(result);
The working Twitter Conversations sample is here, as you can tell it’s really just a wrapper around the Twitter Search API.
Labels: experiment, javascript, mashup, me-me-me