Resources, Resources, Anyone?

So, I've gotten more than one email that says "wish i'd found this sooner". Me too!

I get listed on Ajaxian fairly frequently, but I have to pull a new trick out of my hat each time. Marketing is not my game, and other than Google, that's about it for my major traffic sources. Would anyone like to suggest some sites/blogs/indexes that might list or write about the libraries and tools I have put together here at LivePipe?

If you don't feel like posting a comment, email me at ryan@livepipe.net Any feedback or suggestions would be most appreciated!

7 Replies, Posted June 28th, 2007 at 12:46 pm by Ryan in Opinion

Complex Sorting with PHP

Most often when I need to sort complex data sets, I'm using some sort of SQL backend. Today I needed to do complex sorting of Reflection objects for some auto generating documentation I'm working on for the upcoming Picora release. Namely, I needed to sort methods according to the following criteria:

  • is static
  • visibility
  • alphabetical order

In the usort() examples, I had always been under the assumption (and from other folks code, I know I'm not the only one) that the return values had to be 0, 1 or -1.

Today I learned that is not true at all, and you can come up with your own scoring system with any range, negative or positive. A truncated, but fully functional example:

class PicoraDocumentationClass extends ReflectionClass {
    public function getMethods(){
        $methods = array();
        foreach(parent::getMethods() as $method)
            $methods[] = $this->getMethod($method->name);
        usort($methods,array('PicoraDocumentationClass','sort'));
        return $methods;
    }
    static public function sort($a,$b){
        $a_score = self::scoreFromMethod($a);
        $b_score = self::scoreFromMethod($b);
        return ($a_score == $b_score) ? 0 : ($a_score < $b_score ? -1 : 1);
    }
    static protected function scoreFromMethod(PicoraDocumentationMethod $m){
        return array_sum(array(
            ($m->isStatic() ? -100000 : 0),
            ($m->isPublic() ? -10000 : 0),
            ($m->isProtected() ? -1000 : 0),
            ($m->isPrivate() ? -100 : 0),
            ord(substr($m->name,0,1))
        ));
    }
}

The PicoraController class methods when sorted appear would appear in this order:

  • static public flash
  • static public getFlash
  • static public render
  • static protected redirect
  • static protected renderRSS
  • static protected renderJSON
  • static protected sendFile
  • public afterCall
  • public beforeCall

Just as a quick side note, I'm not sure that scoring in orders of magnitude (100,1000,etc) was entirely nessecary, but it did do the trick right, and consistently. Also note PicoraDocumentationMethod is just a subclass of ReflectionMethod, all of those methods being called are in the Reflection engine.

Coming soon to a Picora project page near you...

2 Replies, Posted June 27th, 2007 at 7:31 pm by Ryan in Programming

Control.Modal 2.1 Released

Also known as "the release that should have been 2.0". Long story short, I didn't test the edge cases nearly well enough and have ended up making a lot fixes in 2.0 → 2.1 than I did from 1.2 → 2.0. What changed?

  • imageTemplate, imageAutoDisplay, evalScripts options have been removed
  • overlay positioning in non IE browsers is CSS based now (== faster)
  • overlay positioning in IE is fixed
  • overlay positioning when the modal is bigger than the document is fixed in all cases that I tested
  • image loading / position timing is fixed across all browsers

A lot of my answers to display bugs in the last week has been "yes yes, I'll get to it", but this release should fix most of the common problems. Still running into problems? Post about it in the forums ASAP!

This is the first release that has fewer options (yes!), and is the first one I feel really great about in a while. As it turns out, modal windows are ripe for edge cases, and aren't particularly easy to write if you hope to cover even half of those cases. This is my shot at it, and I'm finally proud of it.

Control.Modal Project Page

1 Reply, Posted June 23rd, 2007 at 12:20 pm by Ryan in News

Introducting Control Suite & Object Event

Control Suite = Control.Tabs + Control.Modal + Control.TextArea + 3 New Controls!

Today I am pleased to announce that the colllection of widgets and controls I've been developing will now be officially referred to as Control Suite. The three existing controls (Control.Tabs, Control.Modal and Control.TextArea) have all received major upgrades including numerous bug fixes and API improvements, as well as improved documentation, more examples, subversion repositories and RSS changelog feeds.

I am also happy to announce three new controls, Control.Rating, Control.SelectMultiple and Control.ProgressBar have joined the suite. All of the new controls are coded to the same standards as their brethren and include tutorials and complete documentation.

All controls are compatible with Prototype 1.5.x.

Object.Event

Object.Event is a new library that was the genesis of some ideas I had while working on a few larger JavaScript projects. In essence it allows you to create your own events, and call observe() on any object, not just Prototype Element objects. Every Control Suite script already uses it for event handling.

Enjoy the new code and feel free to post bug reports and suggestions as always. Next up Picora, and the forums are going to recieve some much needed attention.

4 Replies, Posted June 18th, 2007 at 4:43 pm by Ryan in News

Control Suite Release Candidates + Object.Event

I've ended up writing six scripts with the "Control." prefix, and they will hence forth be referred to as the "Control Suite" since that's what about a dozen people both publicly and privately refer to them as. I had just run out of room in the subnavigation too.

Control.Modal, Control.Tabs and Control.TextArea are all recieving major updates and bumps to 2.0. Control.Modal and Control.Tabs are already online as release candidates and full documentation. The others will follow ASAP.

So kick the tires in the next few weeks while I get my shit together, and report any bugs to the forums if you find them. Once all the docs are done, and the Object.Event docs are online I'm going to start doing some serious press.

I'd like the release of these to be rock solid, so thanks to those who are already helping me iron out the rough spots.

2 Replies, Posted June 12th, 2007 at 8:25 pm by Ryan in News

iPhone Multi Touch JavaScript API?

So far the talk on the web about the WWDC keynote today is about Safari for windows (I think it's great, but not all that surprising). The big news today for me is that essentially that Ajax is the iPhone SDK. I interpreted this in two opposing ways:

  • Apple isn't ready to support an iPhone SDK in Cocoa
  • Apple fully believes rich internet applications == "way of the future"

I'd really like to believe the latter.

On a side note note, I am trying out the Safari 3 beta and holy shit is it fast. The JavaScript engine implementation is really every bit as good as advertised.

So what I'm stuck wondering is if we'll get some sort of "real" multi touch API to work with. This view point makes some sense to me (summary: they won't provide anything extra for simplicities sake), but that's definitely not my hope.

When looking at patterns of serious innovation in the industry (and let's consider a mass market multi touch device "serious innovation"), nobody bothers to check with a standards body to make sure there is consensus (I'm thinking W3C here, not FCC). So until multi touch devices are a completely common place thing (3 - 5 years), and there are well agreed upon cross browser standards, I hope that apple goes ahead and gives us something to play with even if it's completely proprietary and not standards compliant in any way, because as far as I can see there are no standards for multi touch devices of any type.

Perhaps my main hope is just that I don't have to learn Objective-C. But I do think that if the only available option (for a time) for developing multi touch interfaces for the masses was via HTML/JavaScript/CSS, that it would not only cause web development to be taken more seriously, but it would also make that technology stack more viable for more desktop / device programming in the future.

Dave Hyatt said "I'll have much more to say about this in the coming months." back in january. So hopefully in a few weeks he can start talking.

1 Reply, Posted June 11th, 2007 at 3:51 pm by Ryan in Opinion

Object.Event Draft

This is an observer, observable implementation for Prototype which I'll be releasing in early june. Kick the tires, and tell me what crimes against JavaScript I've commited.

Control.Modal, Control.Tabs and Control.TextArea will all be re-written with this and bumped to 2.0.

This library is the fusion of a few ideas:

  • making any object respond to observe() just like an Element
  • making an interface similar to Ajax.Responders usable to other classes
  • providing a foundation for many to many object relationships in JavaScript MVC applications

if(typeof(Object.Event) == 'undefined'){
    var $stop = new Object();
    Object.Event = {
        extend: function(object){
            object.Responders = {
                _objectEventRespondersSetup: function(){
                    this._responders = this._responders || [];
                },
                register: function(responder){
                    this._objectEventRespondersSetup();
                    this._responders.push(responder);
                },
                unregister: function(responder){
                    this._objectEventRespondersSetup();
                    this._responders = this._responders.without(responder);
                }
            };
            object._objectEventSetup = object.prototype._objectEventSetup = function(event_name){
                this._observers = this._observers || {};
                this._observers[event_name] = this._observers[event_name] || [];
            };
            object.observe = object.prototype.observe = function(event_name,observer){
                this._objectEventSetup(event_name);
                if(!this._observers[event_name].include(observer))
                    this._observers[event_name].push(observer);
            };
            object.stopObserving = object.prototype.stopObserving = function(event_name,observer){
                this._objectEventSetup(event_name);
                this._observers[event_name] = this._observers[event_name].without(observer);
            };
            object.notify = object.prototype.notify = function(event_name){
                var collected_return_values = [];
                var arguments_for_observers = $A(arguments).slice(1);
                var arguments_for_responders = arguments_for_observers.clone();
                arguments_for_responders.unshift(this);
                this._objectEventSetup(event_name);
                try{
                    this._observers[event_name].each(function(observer){
                        collected_return_values.push(observer.apply(observer,arguments_for_observers) || null);
                    });
                    if((this.constructor && this.constructor.Responders) || this.Responders){
                        (this.constructor.Responders ? this.constructor.Responders : this.Responders)._responders.each(function(responder){
                            if(responder[event_name])
                                collected_return_values.push(responder[event_name].apply(responder,arguments_for_responders) || null);
                        });
                    }
                }catch(e){
                    if(e != $stop)
                        throw e;
                }
                return collected_return_values;
            };
        }
    }
}

Sample Life Cycle Callback Usage

The first obvious use case as stated above is in object lifecycles.


Control.Tabs = Class.create();
Object.Event.extend(Control.Tabs);
Object.extend(Control.Tabs,{
    setActiveTab: function(){
        this.notify('beforeChange');
        //tab changing logic here
        this.notify('afterChange');
    }
});
Control.Tabs.Responders.register({
    beforeChange: function(tabs_instance){
        //called for every tabs instance
    }
});
tabs = new Control.Tabs();
tabs.observe('beforeChange',function(){
    //called just for this one tabs instance
});

Usage as Foundation for JavaScript MVC

The talk I am giving in July at the AJAX experience will go into this in much greater depth, but the other use case is treating any internal event in your application as something that is observable. Among the key benefits:

  • very clean separation of concerns
  • you can add an unlimited amount of event handlers as the application grows
  • the code that pertains to a given object can all be written in the same place

For example, let's say we have a UserController object, and a number of things need to be displayed or hidden based on the login state:


UserController = {
    login: function(){
        //do login
        $('element1').show();
        $('element2').show();
    },
    logout: function(){
        //do logout
        $('element1').hide();
        $('element2').hide();
    }
};

If you treat the login and logout methods as events (the events can be called anything, the names don't need to match), you can seperate out the code that deals with the business logic, and the display logic.


UserController = {
    login: function(){
        //do login
        UserController.notify('login',user_data);
    },
    logout: function(){
        //do logout
        UserController.notify('logout');
    }
};
Object.Event.extend(UserController);
//elsewhere in your app
UserController.observe('login',function(){
    $('element1').show();
    $('element2').show();
});
UserController.observe('logout',function(){
    $('element1').hide();
    $('element2').hide();
});

For a 20 line script, this doesn't matter one bit. I'd gladly code it the first way. But if you're dealing with thousands of line of JS spread over multiple files, it's the only way to survive.

4 Replies, Posted May 24th, 2007 at 12:24 pm by Ryan in Programming

RJS is Evil

This is based purely on emotion and frustration, but of all of the evil black magic in Rails, RJS is the grand daddy ruling overload of all lesser magic. There are many crimes, but the big ones are:

  1. Since it auto generates code, you can't search for the JS method names in your source tree.
  2. Since it's the reply to an AJAX request, you can't view source to see what it has generated.
  3. So far I've seen a lot of people use it for visual effects, and style logic, and since it's declared in your views often, it thoroughly contaminates the nice separation of concern Rails otherwise has going for it.

Rails kicks all sorts ass in a dozen different departments, but debugging other people's code that uses RJS has been consistently perplexing, difficult, frustrating and very time consuming.

It takes extra time to structure your HTML and CSS to allow you to write an entirely separate JS behavior layer. But once you do, your app will be cleaner, easier to debug, and a lot more maintainable.

6 Replies, Posted May 18th, 2007 at 1:44 pm by Ryan in Opinion

Speaking at the 2007 SF Ajax Experience

Just wanted everyone to know, that I just got my presentation "Prototype Patterns" officially accepted as part of the Ajax Experience program this year, so to anyone who is attending, I'll see you down there this year!

2 Replies, Posted May 17th, 2007 at 2:12 pm by Ryan in News

Hidden Prototype Goodness

Well, not really hidden. You just have to look hard. The new DOM Builder addition got me looking at the branches in the source tree. These function prototype extensions are absolutely awesome, I hardily hope they make it into the core at some point. There is also a port of Ben Nolan's Behaviour library that seems totally ready.

The core team seems fairly conservative about what goes into a new release. I'm not all for throwing in the kitchen, sink, but there are a limited number of additional things that I find myself wanting, and most of them are present in various patches by now.

Maybe I should change the name of Prototype.Tidbits to Prototype.Liberal and incorporate a few of the unused patches.

1 Reply, Posted May 17th, 2007 at 12:29 pm by Ryan in Programming