10.20.07
LibriVox Player
I've started working on a simple media player for LibriVox.org content. Eventually I'd like to deploy it as an Adobe AIR application to get around the dependence on proxying content through my site (librivox.org doesn't have a crossdomain.xml file...).
LibriVox Player (alpha) Our online pharmacy is the perfect resource for people to get their drugs without any hassles or awkwardness. buy cialis We work hard to make sure you save money every time you shop with us. buy levitrabuy soma At our online store, you pay less and get more. buy viagra
08.14.07
Why Choose Flex/Flash (or other RIA) over AJAX/HTML?
This was a question posed to me by a friend several months ago as I was first getting up to speed on the Adobe Flex platform and expressing some enthusiasm. At the time no immediate answer came to mind. I had just finished up some AJAX work and knew that AJAX is a powerful approach to building rich user-experiences and I scarcely knew the Flex platform had to offer. Today I ran across a post from EffectiveUI that really lays out the advantages of each technology and when it makes sense to use each. Well Done!
http://www.effectiveui.com/blogs/?p=133
Also, I've come across several apps that demonstrate the kind of functionality would be either impossible or prohibitively expensive to develop in AJAX/HTML:
- Picnik-an online image editing service that integrates with Flickr, Picasa, and others
- Papervision3D--Open source 3D engine for the Flash player
- Google Finance-The stock chart here is built on the flash platform
07.30.07
The Web As Database: RSS + ScreenScraping
In the past, if you wanted to use data from another website, you often had to resort to "screenscraping"--or pulling data from a web page by parsing through the raw HTML. In my experience "screenscraping" is a challenge consisting primarily of carefully constructing just the right regex expression to get past a jumble of HTML tags to the content you're interested in.
Today, the situation seems to have improved somewhat with the widespread use of RSS and new services such as Yahoo Pipes, Dapper.net and OpenKapow.
Yahoo Pipes is a really cool service that allows you to combine, filter, transform, etc multiple RSS/XML/CSV feeds into a a single RSS or JSON feed. From an end-user standpoint this offers the benefit of creating a custom RSS feeds based on any number of other feeds you may be interested in, e.g. I have to feeds that return job results from SimplyHired and Dice and have merged them together into a single feed and then filtered that feed based on geographic locations that I would consider accepting. One other great point about Yahoo Pipes is that they've got a crossdomain.xml file which means you can use them as a proxy for Flex/Flash based apps, see this post for the details.
From a developer perspective Yahoo Pipes is cool because it gives you enough control of the RSS feed (i.e. pass in parameters) to essentially treat the resultant RSS and a result set similar to what you'd normally get from a database. The amount of data exposed as RSS, XML or CSV these days really opens up the possibility of combining this data to create interesting applications. Yahoo Pipes helps facilitate the integration of various RSS/XML/CSV sources available on the web to get you just the data you need without the extra trouble of manually pulling it all together yourself.
Now what if there's no RSS/XML/CSV feed for the data you want to get at? It's just embedded in HTML... That's where Dapper.net and OpenKapow come in. They're both services that help you pull content from a webpage and expose it as an RSS feed (in addition to other formats). Dapper.net is purely web-based and is the "lite" version compared to OpenKapow. OpenKapow requires you to download a fairly large NetBeans based client application to construct your feed. Both tools/services prompt you for the target URL from which you'll be pulling info and then guide you through the process of identifying exactly what should be pulled from the page. OpenKapow has a zillion options but the downside is you have to learn how to use them all. It also has a limit on the amount of elements that can be returned in your feed. Dapper.net has fewer features but it purely web-based and easier to use, especially for a straightforward task. I've used both and feel like I'd prefer something kind of in between ... oh well... they're both useful!
Use XSL To Create Advanced Office Docs from a Webapp
Creating Office documents--spreadsheets (Excel), or printable word processing documents (Word), etc--with some kind of dynamic data seems to be a fairly common user requirement for applications, web or otherwise. On the web, creating these documents can be a little tricky. It helps that Word, Excel and others can actually read HTML so without too much trouble you can send a response back with the Content-Type header set to "application/msword" and then just output html and it will open in Word. However, HTML only takes you so far, what if you need to take advantage of some of the more advanced features available in the target application?
I recently worked on a web application which needed to deliver a printable version of a somewhat complex MS Word form. The form was actually the legacy hardcopy form that the web application was working to replace but for interface with some other internal groups I needed to dynamically produce a copy of the legacy form that would be automatically completed with data gathered through the web.
In this case the form was pretty complex and printing was going to be an important factor so I wasn't sure I wanted the browser to be handling my printing for me. I've since used a similar approach to produce dynamic Visio org charts over the web... Munging through Office XML document formats to create XSL stylesheets probably isn't cost/work justified when HTML is a valid option but it's works fairly well when creating documents that will take advantage of advanced features of the office document formats (e.g. org charts in Visio, cell protection in Excel, ...). I haven't tried this with the OpenDocument formats but the priniciple should be about the same.
OK, so here's the basic approach:
1. Define a simple XML structure in which the dynamic parts of the document will reside and write whatever code is necessary to produce that XML when needed.
1. Use the applicable Office application to create the target document. In my case, I was given the existing MS Word document representing the hardcopy form.
2. Save the form as an XML Document. For the MS Office suite I believe this is supported for Office 2003 and above. NOTE: Your users will need to have an office suite supporting the Office XML format in order to make use of this technique!
3. Now Create an XSL stylesheet based on the XML Document
- Start by pasting the entire XML version of the document in to the root processing template
- Find the parts of the document where dynamic data should be inserted and replace those sections with xsl:value-of tags or addtional processing templates called by xsl:apply-templates tags to sub in data from your source XML document.
4. Produce your source XML that represents the dynamic data to be formatted as an Office document
5. Transform the source XML into an office document in XML format using your stylesheet
6. (Optional) If you're working on the web you'll most likely want to stream the result of the XSL transformation directly back to the HTTP response after setting the appropriate headers:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Request("generate") = "true" Then 'Prep the response Response.Clear() Response.ContentType = "application/x-visio" 'Response.AddHeader("Content-Disposition", "inline;filename=orgchart-test.vdx") Response.AddHeader("Content-Disposition", "attachment;filename=orgchart-test.vdx") 'Load XSL Dim xslt As New XslCompiledTransform xslt.Load(Server.MapPath("VisioOrgChart.xslt")) 'Load XML data Dim xml As XmlReader = XmlReader.Create(Server.MapPath("OrgChartSample.xml")) Dim writer As XmlWriter = XmlWriter.Create(Response.Output, xslt.OutputSettings) 'Transform XML to DataDiagramML for Visio xslt.Transform(xml, Nothing, writer, New XmlUrlResolver) Response.End() End If End Sub
06.06.07
Librivox.org Rocks!
As many as times as I've scoured the web looking for free audio books I don't know how I managed to miss http://librivox.org until recently! This is a great site for those who can appreciate classic literature, poetry, and anything else in the public domain but have little time for reading. Summarily, the concept is this: volunteers record themselves reading the text of public domain works and the recordings are aggregated into freely available audio books!
I just recently finished Robert Louis Stevenson's adventure masterpiece Treasure Island. Shiver me timbers!
12.29.05
Automatic Windows Mount via Samba?
OK, I can mount a Windows share just fine with smbmount as a normal user but I've having trouble getting the fstab mount method described here to work... It's been a useful article overall...UPDATE: I got the fstab mount to work (partially), the problem was that I was initializing my wireless connection through an rc.local script AFTER the attempt to mount the samba drive... DUH! Still working on making the mount read-write for all users...
12.23.05
Linux Video Capture
I just spent several hours getting my WinTV PVR USB2 video capture device up and running on my laptop running Fedora Core 4. Here's the really quick rundown of what I did...
- I started with Mike Isley's work on the driver which seems to be the only one active still; or rather the latest iteration on this driver.
- I did not have to set up a kernel source tree as he describes (luckily because I didn't really know how to do that...). Instead I had to install the kernel-devel package with yum and then I discovered that it had downloaded sources for the latest kernel and I needed to update mine before it worked. See the FC4 release notes for more detail.
- Once I had installed the kernel-devel package and made sure my kernel was up to date I was able to run make and make install for both the driver subfolder and the ivtv subfolder in the source code downloaded from Mike Isley's site. I followed his instructions for loading the modules and checking for dependencies etc. The first time I tried to load the module I got an error. I restarted and tried again and got a slightly different error. I was about to give up but decided to just plug the capture device in anyway to see what would happen and it seemed to work.
- UPDATE: When I returned to this a few days later I found it no longer working and spent several more hours in utter frustration unable to figure out what had happened. Many thanks to Julian and Mike from the PVRUSB2 mailing list for their quick responses which set me on track. Apparently my problem was that I was loading the support modules provided by the driver (from Mike Isley's site above) rather than using those provided my kernel. Specifically, I had renamed these modules in favor of those installed in /lib/modules/2.6.14-1.1653_FC4/pvrusb2 by the "make install" task when compiling the driver and support modules. This was my problem... Make sure you have the following modules and delete the modules of the same name in the pvrusb2 folder.
11.26.05
FC4 on Dell Inspiron 8500 - Install Notes
OK, so my XP pro install on my laptop cratered which was all I needed to finally get me to try installing linux on it which I'd been wanting to try for a while. I expected to hit some snags but that didn't really make the late hours combing the web any less frustrating... Maybe this will be useful to someone out there...
- Partitioning- My first error was using automatic partitioning during the install... The install finished but it wouldn't even start up; it erred out with some kernel error related to lvm which I discovered was the logical volume manager. A little googling informed me that using manual rather than automatic partitioning is the way to get around this. Worked for me...
- Wireless- I've got a PCMCIA wireless card made by 3COM (3CRSHPW196). It turns out this guy uses the atmel chipset and fortunately there is support for this hardware through the open source community. I followed andydixon's notes and but have noticed that I can't set the wireless card (eth1) to activate on startup. It works ok if I activate it after I've logged in but I get firmware errors when I set it to load at startup...
- ATI Radeon Mobility- After searching around quite a bit without finding any good HOW-TOs that looked simple enough I just went straight to ati.com and followed the links to their proprietary linux drivers for ATI video cards. They've got an automated installer that did the trick for me but be sure to backup your /etc/X11/xorg.conf file just in case...
- Slow Browsing- OK, I was pretty disappointed to see how slow browsing was on my laptap after the FC4 install. I searched on this a bit and found that IP6 is somehow on by default in FC4 and can slow down network performance. I edited my /etc/modprobe.conf adding the following two lines intended to turn off ip6 in the kernel and rebooted. It seems a little better now...
- alias net-pf-10 off
- alias ipv6 off
- Sound card- The anaconda installer seemed to do a good job of detecting my Intel 82801DB/DBL/DBM sound card but for some reason I couldn't hear any sound. Again, after much googling for me the solution was to unmute the "external amplifier". I did this by running alsamixer from a console. This brings up several ANSI volume guage type things. Use the right arrow to scroll to the right most gauge which should be the "External Amplifier" (scroll past what is originally displayed). Hit "m" to unmute it and then hit "ESC" to quit. That solved it for me.
- XMMS w/MP3 support- Had to add the livna repo for yum to access by creating a livna.repo file in /etc/yum.repos.d with the following contents:
11.22.05
Sorting DOM Nodes
/********************************** ** Functions to sort children of DOM nodes ** written by James Andersen ** 2-25-05 ** No license, if you'd like to mention me, that'd be great ** jandersen.org **********************************/ function sortChildren(el){ //bubbleSortNodes(el.childNodes); insertionSortNodes(el.childNodes); } //sort nodes based on bubbleSort algorithm function bubbleSortNodes(children) { for(var k=0; k < children.length; k++){ if(children[k].nodeType ==3){ children[k].parentNode.removeChild(children[k]); } } var rest = children.length -1; for (var i = rest - 1; i >= 0; i--) { for (var j = 0; j <= i; j++) { if(gt(children[j+1], children[j])) { children[j].parentNode.insertBefore(children[j+1],children[j]); } } } } //sort nodes based on insertion sort algorithm function insertionSortNodes(children) { prepareChildren(children); var i, j, index, tmp; var array_size = children.length; for (i=1; i < array_size; i++) { index = children[i].cloneNode(true); j = i; while ((j > 0) && !gt(children[j-1], index)) { tmp = children[j-1].cloneNode(true); children[j].parentNode.replaceChild(tmp, children[j]); j = j - 1; } children[j].parentNode.replaceChild(index, children[j]); } } //determine whether node1 is greater than node2 function gt(node1, node2){ var sort1 = node1.sortval; var sort2 = node2.sortval; if(typeof node1 == 'object'){ sort1 = node1.sortval ? node1.sortval : getSortVal(node1); node1.sortval = sort1; } if(typeof node2 == 'object'){ sort2 = node2.sortval ? node2.sortval : getSortVal(node2); node2.sortval = sort2; } return (sort1 < sort2); } //get the value of the first text node function getSortVal(mynode){ var tmp; if(mynode.nodeType == 3){ return trim(mynode.nodeValue.toLowerCase()); }else if(mynode.childNodes.length > 0){ for(var i = 0; i < mynode.childNodes.length; i++){ tmp = getSortVal(mynode.childNodes[i]); if(tmp != null && tmp != ''){ return tmp; } } }else{ return null; } } function prepareChildren(children){ if(!children[0].parentNode.prepared){ for(var k =0; k < children.length; k++){ if(children[k].nodeType ==3){ if(trim(children[k].nodeValue) == ''){ children[k].parentNode.removeChild(children[k]); } } } children[0].parentNode.prepared = true; } } function alertArray(children){ var msg = ''; for(var x = 0; x < children.length; x++){ msg += 'Element ' + x + '-->' + (children[x].sortval ? children[x].sortval : children[x]) + 'n'; } alert(msg); } function trim(str) { return str.replace(/^s*|s*$/g,""); }