from __future__ import *
MochiKit 0.60 Released
July 30, 2005 at 11:52 PM | categories: AJAX, javascript, MochiKit | View CommentsI went ahead and created a mailing list for development of and with MochiKit: Google Groups: MochiKit. No public Trac instance yet, but we're planning to have one soon.
MochiKit 0.60 was released yesterday (coincidentally, also my birthday, from which I'm still recovering). All of the changes are relatively minor; mostly just fixing distribution mistakes. For those of you that have the subversion repository checked out, you'll notice that the layout of the distribution zips is slightly different (namely that MochiKit is lib/MochiKit in the zip). That's because JSAN expects a certain distribution layout, and my repository is almost, but not quite, in that form. So, if you had downloaded the zip, I apologize that the examples and tests didn't run out of the box. I may rearrange the repository to match at some point, but for now, this works for me.
Aside from the obligatory bug fixes, there are two new features: a more general version of getElementsByTagAndClassName which doesn't assume you want to ask window.document for tags, plus a "packed" version of MochiKit.
The "packed" version of MochiKit lives at packed/MochiKit/MochiKit.js. It is the entirety of MochiKit concatenated together and comment-stripped. This is about 108k (24k with gzip compression) as-is, but run through a real whitespace-annihilating JavaScript packer it should get considerably smaller, since the coding conventions for MochiKit dictate lots of whitespace (with spaces instead of tabs, at that). While the style guide currently only resides somewhere between my brain and my fingers, it's more or less like what you'd get if PEP 7 and PEP 8 got drunk one night and a baby JavaScript style guide popped out several months later.
packed/MochiKit/MochiKit.js is API compatible with lib/MochiKit/MochiKit.js; meaning, you may refer to it directly with a <script> tag, or you may import it with JSAN.use("MochiKit.MochiKit") or dojo.require("MochiKit.MochiKit") ("MochiKit.* should work in svn, but not 0.60 because I forgot to add a __package__.js). The reason to use the packed MochiKit is that it's a single file and saves a couple round-trips to the server. The packed MochiKit is everything though. If you want an even smaller distribution consisting of a subset of MochiKit, you'll have to roll your own. I believe that Dojo Toolkit may already have this capability (though it's not documented so I don't know how to do it or what state it's in), and there's JavaScript::Librarian coming from the JSAN front. The current stupid packing script is just a temporary means to an end, I intend for it to be replaced by one of those two when they're ready and I've learned how to use them.
I recommend the following process for developing with MochiKit (if only because this is how we do it):
- Development:
- Use MochiKit unpacked, as-is, from the trunk of the repository or the latest distribution. You may even use svn:externals to do this (we do). Using your loading method of choice, load all of MochiKit (e.g. JSAN.use("MochiKit.MochiKit") or <script src="lib/MochiKit/MochiKit.js" type="text/javascript"></script>).
- Production:
- In your staging branch, replace the lib tree with the packed tree from the latest release distribution of MochiKit (or change the svn:externals to point to a release tag in the repo). Ensure that everything works as expected. Deploy!
2005-07-29 v0.60
- Beefed up the MochiKit.DOM test suite
- Fixed return value for MochiKit.DOM.swapElementClass, could return false unexpectedly before
- Added an optional "parent" argument to MochiKit.DOM.getElementsByTagAndClassName
- Added a "packed" version in packed/MochiKit/MochiKit.js
- Changed build script to rewrite the URLs in tests to account for the JSAN-required reorganization
- MochiKit.Compat to potentially work around IE 5.5 issues (5.0 still not supported). Test.Simple doesn't seem to work there, though.
- Several minor documentation corrections
MochiKit 0.50 released
July 28, 2005 at 04:44 AM | categories: AJAX, javascript, MochiKit | View CommentsMochiKit 0.50 has just been released (download here)! In addition to everything that was in SVN yesterday, the documentation has been proofread a bit and Dojo Toolkit package system compatibility was added.
That makes MochiKit the first library to support self-hosting as well as the package systems from JSAN, Dojo Toolkit!
MochiKit is one of the most documented and well tested JavaScript libraries out there. It brings the best idioms for asynchronous and functional programming, iteration, comparison, DOM creation, color space manipulations, etc. from Python (and elsewhere) and applies them to JavaScript. If you're doing client-side web development, you need to check it out. This is just the beginning!
The distribution will be available in JSAN soon. It's already uploaded, but it'll be in the queue until META.json support and the reStructuredText processor are available. This should happen soon, possibly as early as Friday.
MochiKit has a home
July 27, 2005 at 06:07 PM | categories: AJAX, javascript, MochiKit | View CommentsMochiKit is a highly documented and well tested, suite of JavaScript libraries that will help you get shit done, fast. We took all the good ideas we could find from our Python, Objective-C, etc. experience and adapted it to the crazy world of JavaScript.
Check out at at mochikit.com!
System.falseSenseOfSecurity
July 23, 2005 at 04:59 PM | categories: flash, actionscript | View CommentsThe (in)security model [1] available to ActionScript at runtime in Macromedia Flash is absolutely worthless. The only capability that off-domain loaded movies don't have is the ability to read variables out of movie clips elsewhere in the hierarchy. This is almost entirely useless, and is really weak default behavior. If you loaded a movie, you damn well better already trust it! It could be a key logger, a denial of service attack (while (true) {}, getURL("somewhere else")), etc. If you don't trust it, DON'T LOAD IT. You have already lost if you're loading untrusted movies.
If you do trust the movie, and you end up needing the movies to communicate, you have to remember to add the System.security.allowDomain call. This means that you're writing more code for the common case, and the uncommon case is broken anyway.
| [1] | Macromedia Flash MX Security (PDF) |
Round those corners with MochiKit.Visual
July 23, 2005 at 02:30 AM | categories: AJAX, javascript, MochiKit | View CommentsMochiKit received its first "third party" contribution today: MochiKit.Visual. Kevin Dangoor deserves some mad props for this!
Currently, the only spiffy thing MochiKit.Visual does is provide a method to round the corners of HTML elements (demo). Originally it was a port of the Rico code to do the same, but the current implementation bears little similarity to its ancestor.
Three big things (read: almost everything) have changed from the Rico implementation:
- The API to round the corners of an element is a function, roundElement. Not some crazy construct-an-object-just-for-the-side-effects new RoundCorners. Man, some of the JavaScript out there (that people actually use!) should really be making more appearances on the The Daily WTF.
- The Color object is no longer mutable. Again, WTF? I re-engineered the whole thing, so now it really bears more resemblence to AppKit's NSColor. Including the convenient Color.whiteColor(), etc. The Color object also supports a whole lot more stuff than the Rico version; it should be able to handle any color expression in CSS3 (including alpha support and HSL)! Oh, and it has tests, so you know it's not b0rked.
- MochiKit.Visual has documentation.
Come on guys, JavaScript code is fugly. Write some documentation already. I think that if more people wrote docs, they'd realize how bad their APIs are. We've got plenty of docs. In fact, we have complete doc coverage. That's right, everything in MochiKit has documentation you can read without looking at the source!
MochiKit sneak preview
July 21, 2005 at 04:23 AM | categories: AJAX, javascript, MochiKit | View Comments- UPDATE:
- There are more examples, and a real site for MochiKit now. Check out mochikit.com.
As some of you know, MochiKit is a badass lightweight JavaScript library that implements some very cool stuff. We've been working on it from a public subversion repository (thanks red-bean!) for some time now, but haven't had the time to finish the site or package up the distribution . It's very close, and has been more than usable for weeks now.
If you're into edges and bleeding, check out: http://svn.red-bean.com/mochikit/trunk
There's currently exactly one example, Sortable Tables from Scratch with MochiKit, which shows off just a smidge of what MochiKit makes easy. The particular example follows the "unobtrusive javascript" buzz train and adds column sorting to a table that would otherwise display and look just fine if no JavaScript interpreter were available.
The documentation is really shaping up, but is completely unstyled at the moment, so shield your eyes for now. The reStructuredText documentation might be more palatable in the interim.
Look out for MochiKit's imminent release here and on JSAN!
If you want to see more of MochiKit in action, check out its sugar daddy: MochiBot. MochiBot is a tricked out traffic monitoring system for Flash content (that really shines if you're doing a viral campaign or want to watch your game spread over the internet). If you're a Flash developer, you ought to find MochiBot pretty damn cool, and this is just the beginning of many services we're working on at Mochi Media. It's still beta, but it's free. Free is good.
All of the tables in the MochiBot UI are sortable (when that makes sense) and are loaded on the fly by XMLHttpRequest. This makes the interface just about as responsive as it gets for a browser, and you can refresh the data in a fraction of a second if you just can't wait to see how your Flash content is doing. There's even some cute little sparklines on the dashboard!
What happened to YAML?
July 19, 2005 at 02:30 AM | categories: python, perl | View CommentsI remember hearing about YAML quite some time ago, but it seems to have completely disappeared from the Python radar. However, it has become quite popular in Perl (where it's used by Module::Build for metadata) and Ruby (it's a standard feature, even). What's up with that?
I can't even find a complete YAML parser/serializer for Python. The stuff on python.yaml.org looks broken and old, and I haven't seen anything anywhere else.
The only reason I really care is that the JSAN distribution format mirrors CPAN in many ways, META.yml included, and I have to drop down to Perl or Ruby if I want to generate it programmatically! That's not right. Wasn't the progenitor of YAML a Python guy? I remember seeing him on the Twisted lists years ago. What happened?
JavaScript Sucks (volume 2)
July 18, 2005 at 08:07 PM | categories: AJAX, javascript | View CommentsJavaScript sucks (1-14 here):
- Functions can't be used as a prototype, and you can't do new Function(anotherFunction), so you can't "clone" a function unless you reliably have access to its source code(!!).
- While accessing the source code to a function is possible, accessing the scope it was defined in is not. So, you still can't "clone" a function reliably.
- All JavaScript implementations are broken. Some more broken than others (*cough*JScript*cough*). They're all broken in different ways.
- When you run into a broken JScript.. er JavaScript... interpreter, it will either crash, or behave in a completely unexpected way. Good luck debugging either.
- There's no import statement. Good luck writing re-usable code. There are workarounds (e.g. XMLHttpRequest + eval, document.write("..."), etc.), but all are pretty hackish and don't work everywhere all the time or don't get cached by all browsers.
- It's terribly hard to find something that tokenizes JavaScript correctly. Hell, most JavaScript interpreters don't even get that far.
- No coroutines, continuations, generators, threads (please don't!), or equivalent. That means implementing iterators, cooperative multitasking, or anything else asynchronous is only slightly more fun than a good caning.
- Despite its terrible facilities for doing so, writing asynchronous code is necessary. JavaScript interpreters nearly always have the capability to block a browser, sometimes indefinitely.
- When they can't block a browser indefinitely, the user gets a stupid "Do you want to terminate this script?" modal dialog. Regardless of the user's choice, they probably have to terminate the process anyway.
- alert() is a modal dialog, which means that if the script invokes it repeatedly fast enough, you have to terminate your browser's process. Of course, there really isn't a better way to get debugging output.
- JavaScript's caretakers don't seem to be interested fixing any of its problems, just adding new ones. Optional type declarations, classes and interfaces, inline XML, etc. Whee!
Javascript OO Primer
July 18, 2005 at 04:10 AM | categories: AJAX, javascript | View CommentsPrototypes
JavaScript is a non-traditional Object Oriented language in that it uses prototype based, rather than class based inheritance. Some other languages that implement prototype-based OO are Self, Lua, Io, NewtonScript, Slate, and Prothon.
The basic premise of prototype-based OO is that new objects are created from similar objects, much like using a copy machine. The new object is largely indistinguishable from the prototype used to create it. It is possible to modify the new object without affecting the prototype, and this new object may be used as the prototype for other objects.
Functions
In JavaScript, functions are first-class objects like any other. They may be stored in variables, passed as arguments, and properties may be added or removed from them dynamically (e.g. func.foo = "bar"). Functions are also the building block for creating new objects. There are three different syntaxes for invoking a function:
- Function Call:
func(a, b)
Execute the body of func with two arguments, a and b, and the special this variable set to the global object (typically window in a browser). The value of this expression will be the value given by the terminal return statement, or undefined if no return statement is reached before the execution completes.
Note that even if func is a local variable in another function's scope, this will still be set to the global object!
- Method Call:
obj.func(a, b)
Execute the body of func with two arguments, a and b, and special this variable set to obj. The value of this expression will be the value given by the terminal return statement, or undefined if no return statement is reached before the execution completes.
Note that this is actually a different syntax entirely than the function call. func is not bound to obj in any way, and that a reference to func as a local variable or on another object will behave differently if called!
- Constructor:
new func(a, b)
Execute the body of func with two arguments, a and b, and special this variable set to a new object (newObject) cloned from func.prototype. The value of this expression is always newObject. If any return statement is reached during execution, its value is ignored.
The expression (newObject instanceof func) will evaluate to true so long as func.prototype is not replaced with another object. Additionally, (newObject instanceof T) will evaluate to true for all values of T where (func.prototype instanceof T) evalutes to true.
newObject is a copy-on-write clone of func.prototype. This means that any property set on newObject will not propagate to func.prototype. Conversely, any property added or changed on func.prototype will propagate to newObject, unless that property has been set on newObject.
All objects, including func.prototype, have this prototype property delegation behavior. This means that properties from func.prototype's prototype will propagate to newObject unless otherwise set by func.prototype or newObject, and so on. This prototype property delegation chain terminates with Object.prototype, so properties set on Object.prototype are accessible (and enumerable) from every object in the whole interpreter! Due to this, it's generally wise to avoid modifying Object.prototype (or really, any built-in prototype) whenever possible.
Demonstration
// set up the prototype func.prototype.protoProperty = 1; func.prototype.anotherProperty = 2; // create a new object from the prototype var newObject = new func(a, b); // the prototype's properties are visible on newObject assert( newObject.protoProperty == 1 ); assert( newObject.anotherProperty == 2 ); // newObject can be modified without affecting its prototype newObject.anotherProperty = 3; assert( newObject.anotherProperty == 3 ); assert( func.prototype.anotherProperty == 2 ); // func.prototype can be modified, and it will affect // newObject unless the modified properties were written to // in newObject func.prototype.protoProperty = 0; func.prototype.anotherProperty = 1; assert( newObject.protoProperty == 0 ); assert( newObject.anotherProperty == 3 );
Airport Express Hates Me
July 18, 2005 at 03:21 AM | categories: python, iPod, macosx, PyObjC | View CommentsA few weeks ago I began my move from New York to San Francisco by... moving to Hawaii. For the summer, anyhow. In shipping, my trusty old Klipsch computer speakers seem to have eaten it. I tried hooking them up, and after some really painful shrieking, they worked for a few minutes.. until they caught fire. Really. My ex-favorite music spewing devices have found themselves a nice retirement home in a landfill somewhere on Maui.
I tried playing iPod DJ with the stereo here, but that doesn't really work very well for long stretches. It was time to break down and finally get an Airport Express. I looked around online a bit, and shipping was just ridiculous to Hawaii from most places (e.g. Smalldog wanted $95 S+H to ship a $120 refurb Airport Express to Kihei!). Fortunately, there is a Mac store in town, so I was able to pick one up at a reasonable price.
Immediately after bringing it home I plugged it in via ethernet, and tried to update the firmware. A few hundred times. It just wouldn't take. The Airport Admin utility would not do it. From either of my Macs. It'd try, and it would fail after a minute or two without even an error code. Eventually I found a link to download a standalone firmware updater application, which updated the firmware first try. Go figure. Certainly not the typical Apple experience.
Once it was updated and hooked up, it worked great. 90% of the time, anyway. iTunes will only play files that it natively understands through the Airport Express. Apparently, iTunes has decided that some of my MP3s were QuickTime files, and refused to play them through Airport Express, which means they were coming out of the built-in speakers. That didn't work out so well. I guess that sucks for people who care about OGG and other formats supported only via QuickTime.
It turns out that the reason these files were picked up as QuickTime is that iTunes has MPEG type code hate. MacAMP and SoundJam of yesteryear used MPEG, yet iTunes wants to see MPG3. It took me a lunch break to come up with a good way to track down all of these files without writing a lot of code. It was actually rather simple: the iTunes Music Library XML dump.
~/Music/iTunes/iTunes Music Library.xml is a brain-dump of iTunes that it creates every so often (on quit, I believe). It keeps track of almost everything that iTunes knows, all of your file's URLs, their type and creator codes, comments, metadata, you name it. It's also in plist format, which is extremely easy to work with from Python.
Assuming you have PyObjC installed, you can break into a Python interpreter and screw around with this rather easily:
% python
Python 2.4.1 (#2, Mar 31 2005, 00:05:10)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Foundation import *
>>> import os
>>> dbFile = os.path.expanduser("~/Music/iTunes/iTunes Music Library.xml")
>>> db = NSDictionary.dictionaryWithContentsOfFile_(dbFile)
>>> type(db)
<objective-c class NSCFDictionary at 0x30a4b0>
With the database in hand, we can start poking around:
>>> track = db[u'Tracks'].itervalues().next()
>>> print track.description()
{
Album = "Live Code";
Artist = "Front 242";
"Artwork Count" = 1;
"Bit Rate" = 160;
...
In these track dictionaries, there are two keys relevant to this exercise: u'File Type', and u'Location'. File type is the integer representation of the type code of the track. Location is the URL of the track. With this, it's pretty easy to pick out the locations of the tracks that are set to the MPEG type code:
>>> import struct
>>> badPaths = [
... NSURL.URLWithString_(track[u'Location']).path()
... for track in db[u'Tracks'].itervalues()
... if track.get(u'File Type') == struct.unpack('>I', 'MPEG')[0]
... ]
>>> badPaths[0]
/Volumes/...
With this, all we need to do is change the type and creator. Fortunately, that's easy. We'll use the iTunes creator code hook, and the expected MPG3 type code:
>>> import MacOS >>> for path in badPaths: ... MacOS.SetCreatorAndType(path, 'hook', 'MPG3') ...
And with that, my Airport Express does just about everything I'd have expected it to out of the box ;)
Next Page ยป