(function(){//enable.js

/*****************************************************************************\
Enable JavaScript Library
Utilities and CSS style selction for Facebook-encoded JavaScript

Enable is a JavaScript Library for the facebook platform to allow for better
document manipulation and ease of code. Enable uses syntax and methods similar
other JavaScript libraries you may alrady know and love. Using Enable, you will
be able to code faster, code elegantly, and code better interface effects.

The Enable function is a selection method. It recieves one argument, that may
be an element reference, an array of element references, a CSS selctor string,
or an XHTML tag. Regardless of the argument passed, Enable returns an array.

If a single element reference is passed as an argument, Enable will return an
array containing that one element. If an array of element references is passed,
Enable will return an Array containing those elements. When a CSS selector is
passed, enable will return an array of elements that match the selector. And
when an XHTML tag is passed, Enable will create a new element.

If you have used other JavaScript Libraries already, you will be familiar with
this style of element selection. The reason Enable only returns arrays, is
that the arrays returned have been augmented with new methods. These are the
methods you will use with enable to manipulate elements and element groups.

The Enable provides an "each" method which can be used to iterate though
all of its contained elements by passing each a function argument. The function
passed has access to the element via the "this" keyword variable. The same
variable-scheme is used for callback functions and other nested methods also.

Some methods can assign new attribute values or return the values, depending on
how they are used. When using the "css" method for instance, one may pass a
object containing key:value pairs, which will assign all elements within that
Enable array with those new style rules. When passing a string to the "css"
method, the result is different. Instead "css" will return the value of the
rule represented in the string.

All Enable methods will return the Enable Array unless otherwise specified.
For methods that create or replace elements, the new elements or replacements
will be returned within the Enable Array in place of their former elements.

When a value is returned from an Enable Array such as when passing the "css"
method a string, the returned value is always from the first element in the
array.

Further documentation is available at: http://apps.facebook.com/fbenable/
--> [ryank] LIES. NO DOCS THERE.

Enable methods:

    animate( object, integer, function, boolean )
        Animates elements using css rules. The first argument must be an object
        containing css key:value pairs, just as in "css()", which will be the
        final values at the end of the animation. The second argument is the
        duration of the animation in milliseconds (1000 = 1 second). The third
        argument is an optional callback which will be completed after the
        animation completes. When two or more animate methods are called, they
        will play out through a queue in the order they were added. The one
        exception to this rule, is if the fourth argument passes true, in which
        case the queue will be deleted, and the animation will play
        immediately.


    append( element )
        Inserts a new child into an element as the last child.

    appendTo( element )
        Inserts an element as a new last child node into another element.

    attr( string )
        Returns the value of the attribute represented in the passed string.

    attr( object )
        Sets the attribute value of an element based on the key:value pairs
        within the object passed in. If a key is passed for which there is no
        value setter for though FBJS, that key will be silently ignored.

    bind( string, function )
        Attaches an event to an element. Receives a string containing the event
        type and a function to assign to the event. The this variable in the
        function will refer to element the event was attached to.

    checked()
        Returns true if an element is checked or false if otherwise.

    checked( boolean )
        Checks or unchecks checkboxes based on passed boolean.

    children( expr )
        Get a set of elements containing all of the immediate children of each
         of the matched set of elements, optionally filtered by expr

    contents()
      Return all children of the matched elements

    click( fn )
        Binds a function to the click event of each matched element.

    css( string )
        Provides access to the css values of an element. When a css attribute
        string is passed, such as "borderWidth", the corresponding value will be
        returned. Note the JavaScript camel-case key is used, and not the
        hyphenated version used in css.

    css( object )
        An object may also be passed to css, which will set the css values for
        the element using the key:value pairs, such as "{fontSize: 14px}".

    debind( string )
        Removes all events that have a type, matching the passed string.

    disable( boolean )
        Disables or enables form elements in the current collection based on
        the boolean passed in.

    each( function )
        Iterates through the elements contained within the Enable Array. The
        function passed each has access to the elements via the "this" variable.

    empty()
        Removes all children of an element.

    filter( expr )
        Removes elements from the Enable Array, leaving only those values which
        match expr.

    filter( function )
        Removes elements from the Enable Array, leaving only those values for
        which the function passed returns true.  The function is run in
        the same way as "each"

    find( string )
        Selects child elements via the css selector passed to it and returns
        a new Enable Array containing any elements that match the selector.

    get( index )
        Returns all bare elements inside the current collection if no index is
        passed; otherwise returns the bare element at the given index.

    hide()
        An animation that will hide the element by setting the css display value
        to none.

    html( string )
        Inserts HTML into an element using FBML. Note, that only elements my be
        inserted and not text.

    offset()
        Returns an object with four properties. Left, top, width, and height.
        The left and top values are relative to the root facebook canvas
        element. The width and height values refer to the offset, which includes
        width, padding, margin, and border.

    offset( string )
        Returns the value off the offset parameter corresponding to the string
        passed to it. Options are left, top, width, and height. If no string is
        passed, the method will return an object with all four properties. The
        left and top values are relative to the root facebook canvas element.

    parent( selector )
        Returns the immediate parent of the first element in the collection if
        no selector is given. Returns the first ancestor that matches the
        selector if one is given.

    pause( integer, function )
        Adds a pause to the queue with an optional callback.

    remove()
        Removes an element from the DOM.

    replace( element )
        Replaces an element with another element, which was passed to it.

    select()
        Returns the selected text from a text field. The text is returned in the
        for of an object with two properties, "start" and "stop", which refer to
        the index position of the first and last selected characters.

    select( object )
        Method for setting selected text within text fields. Receives an object
        with "start" and "end" properties, referring to the indexes in the value
        of the text field.

    show( string )
        An animation that will reveal the element by setting the css display
        value to the string passed. If no string is passed, the default value
        is block.

    size()
        Returns the number of matched elements in the collection.

    submit( fn )
        Binds a function to the submit event of each matched element.

    text( string )
        Sets the text node of an element.

    toggle()
        Hides the element or, if the element is already hidden, it will be
        revealed.

    unbind( string, function )
        Removes an event from an element. Receives a string containing the event
        type and a function or reference to remove.

    val()
        Returns the value of the element. In the case of select boxes, method
        will return the value of the selected option.

    val( string )
        Sets a new value to elements.

    width()
        Returns the width of an element, including padding.

    width( string )
        Assigns only a new width of an element. This will include padding,
        but will not alter it.

    wrap( string )
        Encases an element in a newly created element place in the same
        position within the DOM. This method returns the new elements.

*******************************************************************************/

Enable = function(input) {                       //pass a selector or element
    input = Enable.find(input);                   //creates an array of those elements
    input = Enable.extend(input,Enable.methods);  //extends that array with methods
    return input;                                //returns array ready to use
};

/******************************************************************************\
    Referances & Namespaces
*******************************************************************************/

//facebook's attribute accessors
Enable.accessors = {
    accesskey    : ['getAccessKey',     'setAccessKey'],
    action       : ['getAction',        'setAction'],
    checked      : ['getChecked',       'setChecked'],
    className    : ['getClassName',     'setClassName'],
    cols         : ['getCols',          'setCols'],
    dir          : ['getDir',           'setDir'],
    disabled     : ['getDisabled',      'setDisabled'],
    href         : ['getHref',          'setHref'],
    id           : ['getId',            'setId'],
    index        : ['getSelectedIndex', 'setSelectedIndex'],
    name         : ['getName',          'setName'],
    method       : ['getMethod',        'setMethod'],
    readonly     : ['getReadOnly',      'setReadOnly'],
    rows         : ['getRows',          'setRows'],
    selected     : ['getSelected',      'setSelected'],
    src          : ['getSrc',           'setSrc'],
    tabindex     : ['getTabIndex',      'setTabIndex'],
    tag          : ['getTagName',       false],
    tagname      : ['getTagName',       false],
    target       : ['getTarget',        'setTarget'],
    title        : ['getTitle',         'setTitle'],
    type         : ['getType',              'setType'],
    value        : ['getValue',         'setValue'],
    scrollwidth  : ['getScrollWidth',   false],
    scrollheight : ['getScrollHeight',  false],
    scrollleft   : ['getScrollLeft',    'setScrollLeft'],
    scrolltop    : ['getScrollTop',     'setScrollTop']
};

//units used when assigning CSS rules
Enable.cssUnits = {
    width         : 'px',
    height        : 'px',
    left          : 'px',
    top           : 'px',
    right         : 'px',
    bottom        : 'px',
    padding       : 'px',
    paddingTop    : 'px',
    paddingRight  : 'px',
    paddingBottom : 'px',
    paddingLeft   : 'px',
    margin        : 'px',
    marginTop     : 'px',
    marginRight   : 'px',
    marginBottom  : 'px',
    marginLeft    : 'px',
    fontSize      : 'px',
    lineHeight    : 'px'
};

//method prototype
Enable.methods = {};

//element creation namespace
Enable.view    = {};

//environment settings access namespace
Enable.setting = {};

//facebook root element reference
Enable.canvas = document.getRootElement();

//decoy function
Enable.stub = function(){};

/******************************************************************************\
    Enable.find
    Collects elements and returns them within an array from a passed string,
    element, or an array of elements.
*******************************************************************************/
Enable.find = function(input) {
    var elements = [];
    if (!input) {
    }
    else if (input.shift) {
        for (var i = 0, all = input.length; i < all; i++) {
            var more = Enable.find(input[i]);
            while (more.length) {
                var ele = more.shift();
                elements.push(ele);
            }
        }
    }
    else if (input.getChildNodes) {
        elements.push(input);
    }
    else if (input.split) {
        if (input.match(/^<.*>$/)) {
            elements.push(Enable.create(input));
        }
        else {
            elements = Enable.getElementsBySelector(input);
        }
    }
    return elements;
};

/******************************************************************************\
    Enable.extend
    Object extending
*******************************************************************************/
Enable.extend = function(student,teacher) {
    for (var i in teacher) {
        if (teacher.hasOwnProperty(i)) {
            student[i] = teacher[i];
        }
    }
    return student;
};

/******************************************************************************\
    Enable.each
    Generic Array iterator
*******************************************************************************/
Enable.each = function(array,loop) {
    for (var i = 0, all = array.length; i < all; i++) {
        loop.apply(array[i]);
    }
};


/******************************************************************************\
    Enable.create
    Constructs an elements rfom a XHTML string.
*******************************************************************************/
Enable.create = function(newHTML) {
    var womb = document.createElement('div');
    womb.setInnerXHTML(newHTML);
    return womb.getChildNodes()[0];
};

/******************************************************************************\
    Enable.queue
    Creates an array for queue-controlled functions
*******************************************************************************/
Enable.queue = function(){
    var queue = [];
    queue.active = false;
    //removes all functions from the queue and returns them
    queue.empty = function() {
        var spill = [];
        while (queue.length) {
            spill.push(queue.shift());
        }
        return spill;
    };

    //adds a new function to the queue and starts processing
    queue.add = function(func,yield,priority){
        func.yield = yield || false;
        func.priority = priority || 100;
        queue.push(func);
        queue.sort(function(a,b){
            return a.priority - b.priority;
        });
        if(!queue.active){
            queue.next();
        }
    };

    //processes the next item in the queue, if the queue is ready
    queue.next = function(){
        if(queue.length){
            var func = queue.shift();
            queue.active = true;
            func();
            if (!func.yield) {
                queue.next();
            }
        }
        else {
            queue.active = false;
        }
    };
    return queue;
};

/******************************************************************************\
    Enable.interval
    Creates an interval object to simplify control
*******************************************************************************/
Enable.interval = function(func,delay) {
    var interval = {};
    var running = false;
    interval.start = function(time) {
        if (interval) {
            clearInterval(running);
            running = false;
        }
        running = setInterval(func, time || delay);
    };
    interval.stop = function() {
        if (interval) {
            clearInterval(running);
            running = false;
        }
    };
    interval.status = function() {
        return !!interval;
    };
    return interval;
};

/******************************************************************************\
    Enable.ajax
    Makes an HTTP request
*******************************************************************************/
Enable.ajax = function(type,url,data,success,error) {
    var ajax = new Ajax();
    ajax.responseType = Ajax[type.toUpperCase()] || Ajax.RAW;
    ajax.ondone = success || false;
    ajax.onerror = error || false;
    ajax.post(url, data);
};

/******************************************************************************\
    Enable.methods
    Extendable Enable Array methods.
*******************************************************************************/
Enable.methods.size = function() {
    return this.length;
};

//iterates though all elements
Enable.methods.each = function(func) {
    for (var i = 0, all = this.length; i < all; i++) {
        func.apply(this[i]);
    }
    return this;
};

//transform an array into another array
Enable.methods.map = function(func) {
    var newArray = [];
    this.each(function() {
        newArray.push(func.apply(this));
    });
    return Enable(newArray);
};

//return all bare elements in the current collection
Enable.methods.get = function(ix) {
    var ret = [];
    for (var i = 0, all = this.length; i < all; i++) {
        ret.push(this.shift());
    }
    if (ix === undefined) {
        return ret;
    }
    else {
        return ret[ix];
    }
};

//get the parent element or, optionally, an ancestor matching a given selector
Enable.methods.parent = function(selector) {
    var parent = this[0].getParentNode();
    if (selector === undefined) {
      return Enable(parent);
    }
    var els, grand;
    while (parent != document.getRootElement()) {
        grand = parent.getParentNode();
        els = Enable.getElementsBySelector(selector, grand);
        if (Enable(els).filter(function () {
                          return this == parent;
                        }).length == 1) {
            return Enable(parent);
        }
        parent = grand;
    }
};

//remove all elements
Enable.methods.empty = function() {
    for (var i = 0, all = this.length; i < all; i++) {
        this.shift();
    }
    return this;
};

//remove all elements
Enable.methods.empty = function() {
    this.each(function() {
        while (this.getFirstChild()) {
            this.removeChild(this.getFirstChild());
        }
    });
    return this;
};

//add elements
Enable.methods.add = function(addition) {
    Enable.find(addition);
    for (var i = 0, all = addition.length; i < all; i++) {
        this.push(addition[i]);
    }
    return this;
};

//accesses element attributes
Enable.methods.attr = function(query) {
    var fbMethods = Enable.accessors;
    if (query.split) {
        if (this[0] && this[0][fbMethods[query][0]]) {
            var val = this[0][fbMethods[query][0]]() || false;
            return val === true ? query : val;
        }
    }
    if (query.hasOwnProperty && !query.split && !query.join) {
        this.each(function() {
            for (var i in query) {
                if (query.hasOwnProperty(i)) {
                    if (this[fbMethods[i][1]]) {
                        this[fbMethods[i][1]](query[i]);
                    }
                }
            }
        });
        return this;
    }
};

//accesses element style rules
Enable.methods.css = function(query) {
    if (query.split) {
        if (this[0] && this[0].getStyle) {
            if (Enable.cssUnits[query]) {
                return parseInt(this[0].getStyle(query), 10) || this[0].getStyle(query);
            }
            else {
                return this[0].getStyle(query);
            }
        }
    }
    if (query.hasOwnProperty && !query.split && !query.join) {
        for (var i in query) {
            if (query.hasOwnProperty(i) && typeof(query[i]) == 'number' && Enable.cssUnits[i]) {
                query[i] = query[i] + 'px';
            }
        }
        this.each(function() {
            this.setStyle(query);
        });
        return this;
    }
};

//adds a function to element's animation queue
Enable.methods.enqueue = function(func,yield,wait) {
    this.each(function() {
        var actor = this;
        actor.queue = actor.queue || Enable.queue();
        actor.queue.add(function() {
            func.apply(actor);
        },yield,wait);
    });
    return this;
};

//adds animations to element's queue
Enable.methods.animate = function(after, duration, callback, force) {
    this.each(function() {
        var actor = this;
        actor.queue = actor.queue || Enable.queue();
        if (force){
            actor.queue.empty();
        }
        actor.queue.add(function() {
            var line = Animation(actor);
            for (var i in after) {
                if (after.hasOwnProperty(i)) {
                    line.to(i, after[i]);
                }
            }
            line.duration(duration).checkpoint(1, function() {
                if (callback) {
                    callback.apply(actor);
                }
                actor.queue.next();
            }).go();
        },true);
    });
    return this;
};

//pauses element's animation queue
Enable.methods.pause = function(duration, callback) {
    this.animate({}, duration, callback);
    return this;
};

//reveals element
Enable.methods.show = function(display) {
    display = display || 'block';
    this.each(function() {
        var actor = this;
        actor.queue = actor.queue || Enable.queue();
        actor.queue.add(function() {
            actor.setStyle('display',display);
        });
    });
    return this;
};

//hides element
Enable.methods.hide = function() {
    this.each(function() {
        var actor = this;
        actor.queue = actor.queue || Enable.queue();
        actor.queue.add(function() {
            actor.setStyle('display','none');
        });
    });
    return this;
};

//toggles element from hidden an shown
Enable.methods.toggle = function() {
    this.each(function() {
        var state = this.getStyle('display');
        if (state == 'none') {
            this.setStyle({
                display: 'block'
            });
        }
        else {
            this.setStyle({
                display: 'none'
            });
        }
    });
    return this;
};

//accesses checkbox element
Enable.methods.checked = function(toggle) {
    if (toggle !== undefined) {
        this.each(function() {
            this.setChecked(toggle);
        });
        return this;
    }
    else {
        if (this[0] && this[0].getChecked) {
            return this[0].getChecked();
        }
    }
};

//enables/disables form element
Enable.methods.disabled = function(toggle) {
    if (toggle !== undefined) {
        this.each(function() {
            this.setDisabled(toggle);
        });
        return this;
    }
    else {
        if (this[0] && this[0].getDisabled) {
            return this[0].getDisabled();
        }
    }
};

//accesses element's value
Enable.methods.val = function(newValue) {
    if (newValue) {
        this.each(function() {
            this.setValue(newValue);
        });
        return this;
    }
    else {
        if (this[0].getTagName().toLowerCase() == 'select') {
            var selectedIndex = this[0].getSelectedIndex();
            var children = this[0].getChildNodes();
            return children[selectedIndex].getValue();
        }
        else if (this[0] && this[0].getTagName().toLowerCase() == 'input' && this[0].getType().toLowerCase() == 'radio') {
            if (this[0].getChecked()) {
                return this[0].getValue();
            }
            else {
                var choices = Enable.getElementsBySelector('input[name='+ this[0].getName() +']');
                for (var i = 0, all = radios.length; i < 0; i++) {
                    if (choices[i].getChecked() && choices[i].getType().toLowerCase() == 'radio') {
                        return choices[i].getValue();
                    }
                }
                return false;
            }
        }
        else {
            return this[0].getValue();
        }
    }
};

//replacements element in the document
Enable.methods.replace = function(replacement) {
    replacement = replacement[0] || replacement;
    var replacements = [];
    this.each(function(){
        var child = this;
        var sibling = replacement.cloneNode(true);
        var parent = child.getParentNode();
        parent.insertBefore(sibling, child);
        replacements.push(sibling);
        parent.removeChild(child);
    });
    this.add(replacements);
    return this;
};

//removes element from the document
Enable.methods.remove = function() {
    this.each(function(){
        var parent = this.getParentNode();
        parent.removeChild(this);
    });
    return this;
};

//sets element's text node
Enable.methods.text = function(newText) {
    newText = newText.toString();
    newText = newText.replace(/\&/g,'&#38;');
    newText = newText.replace(/\</g,'&#60;');
    newText = newText.replace(/\>/g,'&#62;');
    newText = newText.replace(/\n/g,'<br />');
    newText = '<span class="innerText">'+newText+'&#160;</span>';
    this.each(function(){
        //this.setTextValue(newText); //doesnt support new lines.
        this.setInnerXHTML(newText);
    });
    return this;
};

//sets element's innerHTML
Enable.methods.html = function(newHTML) {
    this.each(function(){
        this.setInnerXHTML(newHTML);
    });
    return this;
};

//inserts a new child into element
Enable.methods.append = function(child) {
    // child = child[0] || child;
    // child = (typeof child.length != "undefined") ? child[0] : child;
    try {
      child = child[0] || child;
    }
    catch(ex) {
      child = child;
    }
    this.each(function(){
        this.appendChild(child);
    });
    return this;
};

//inserting element into a parent
Enable.methods.appendTo = function(parent) {
    parent = parent[0] || parent;
    this.each(function(){
        parent.appendChild(this);
    });
    return this;
};

//inserts element after a sibling
Enable.methods.insertAfter = function(replacement) {
    replacement = replacement[0] || replacement;
    this.each(function(){
        var child = this;
        var sibling = replacement.cloneNode(true);
        var parent = child.getParentNode();
        var children = parent.getChildNodes();
        if (!child.getNextSibling()) {
            parent.appendChild(sibling);
        }
        else {
            parent.insertBefore(sibling, child.getNextSibling());
        }
    });
    return this;
};

//inserts element before a sibling
Enable.methods.insertBefore = function(ref) {
    ref = ref[0] || ref;
    this.each(function(){
        var child = ref;
        var sibling = this;
        var parent = child.getParentNode();
        parent.insertBefore(sibling, child);
    });
    return this;
};

//inserts element into a newly created parent
Enable.methods.wrap = function(wrapper) {
    var wraps = [];
    this.each(function(){
        var wrap = Enable.create(wrapper);
        var parent = this.getParentNode();
        parent.insertBefore(wrap,this);
        wrap.appendChild(this);
        wraps.push(wrap);
    });
    return Enable(wraps);
};

//accesses element width
Enable.methods.width = function(newWidth) {
    if (!newWidth && this[0]) {
        var width = this[0].getClientWidth() || this[0].getStyle('width') || this[0].getOffsetWidth() || false;
        width = typeof(width) == 'string' ? parseInt(width,10) : width;
        return width;
    }
    else {
        this.each(function() {
            var padding = parseInt(this.getStyle('paddingLeft'),10);
            padding += parseInt(this.getStyle('paddingRight'),10);
            var width = parseInt(newWidth,10) - padding;
            this.setStyle('width',width+'px');
        });
        return this;
    }
};

//accesses element height
Enable.methods.height = function(newHeight) {
    if (!newHeight && this[0]) {
        var height = this[0].getClientHeight() || this[0].getStyle('height') || this[0].getOffsetHeight() || false;
        height = typeof(height) == 'string' ? parseInt(height,10) : height;
        return height;
    }
    else {
        this.each(function() {
            var padding = parseInt(this.getStyle('paddingTop'),10);
            padding += parseInt(this.getStyle('paddingBottom'),10);
            var height = parseInt(newHeight,10) - padding;
            this.setStyle('height',height+'px');
        });
        return this;
    }
};

//collects element offset data
Enable.methods.offset = function( prop ) {
    var relative = document.getRootElement();
    var space = this[0];
    var parent = space.getParentNode();
    var offset = false;

    //facebook's get offset is broken, and doesn't report margins properly.
    //This hack clones the element into a new container and measures that instead
    if (space && space.getOffsetWidth && space.getStyle('position') != 'absolute') {
        var clone = space;
        var secondOpinion = Enable.create('<div></div>').setStyle({
            'padding' : '1px',
            'margin'  : '0px',
            'float'   : 'left'
        });
        var incorrect = {
            top          : space.getAbsoluteTop() - relative.getAbsoluteTop(),
            left         : space.getAbsoluteLeft() - relative.getAbsoluteLeft(),
            width        : space.getOffsetWidth(),
            height       : space.getOffsetHeight()
        };
        parent.insertBefore(secondOpinion,space);
        secondOpinion.appendChild(clone);
        var correct = {
            top          : secondOpinion.getFirstChild().getAbsoluteTop() - 1 - relative.getAbsoluteTop(),
            left         : secondOpinion.getFirstChild().getAbsoluteLeft() - 1 - relative.getAbsoluteLeft(),
            width        : secondOpinion.getOffsetWidth() - 2,
            height       : secondOpinion.getOffsetHeight() - 2
        };
        parent.insertBefore(space,secondOpinion);
        parent.removeChild(secondOpinion);

        //now we need to see if theres a margin going unused, and substract it from the height
        var difference = {
            height : correct.height - incorrect.height,
            width  : correct.width - incorrect.width
        };
        //or maybe not? you decide.
        //correct.margin = correct.height - difference.height;

        correct.topOff    = correct.top - incorrect.top;
        //correct.marginBottom = correct.margin - correct.marginTop;

        offset = prop ? correct[prop.toLowerCase()] : correct;
    }
    //else if the element is positioned absolute, the hack wont work right.
    //Then use this code, which is how it should haved worked to begin with.
    else if(this[0] && this[0].getOffsetWidth ){
        var props = {
            top          : this[0].getAbsoluteTop() - relative.getAbsoluteTop(),
            left         : this[0].getAbsoluteLeft() - relative.getAbsoluteLeft(),
            width        : this[0].getOffsetWidth(),
            height       : this[0].getOffsetHeight()
        };
        if (prop) {
            offset = props[prop.toLowerCase()];
        }
        else {
            offset = props;
        }
    }
    return offset;
};

//adds an event to element
Enable.methods.bind = function(eventType,func) {
    this.each(function() {
        this.addEventListener(eventType,func);
    });
    return this;
};

//removes an event from element
Enable.methods.unbind = function(eventType,func) {
    this.each(function() {
        this.removeEventListener(eventType,func);
    });
    return this;
};

//removes events from an element
Enable.methods.debind = function(eventType) {
    if (eventType) {
        this.each(function() {
            this.purgeEventListeners(eventType);
        });
    }
    return this;
};

Enable.methods.click = function(func) {
  this.bind('click', func);
  return this;
};

Enable.methods.submit = function(func) {
  this.bind('submit', func);
  return this;
};

//selects child elements within element
Enable.methods.find = function(selector) {
    var replacements = Enable();
    this.each(function() {
        var kiddies = Enable.getElementsBySelector(selector,this);
        replacements.add(kiddies);
    });
    return Enable(replacements);
};

Enable.methods.contents = function() {
  return Enable(Enable.getElements(this));
};

//selects direct children elements
Enable.methods.children = function(expr) {
  var replacements = Enable();
  this.each(function() {
    var parentNode = this;
    var kids = Enable(parentNode.getChildNodes());
    if (expr)
      kids = kids.filter(expr);
    replacements.add(kids);
  });
  return Enable(replacements);
};
//removes elements that don't match the filter
Enable.methods.filter = function(cond) {
    var keepers = [];
    if(typeof(cond) == 'function') {
      this.each(function() {
          if (cond.apply(this)) {
              keepers.push(this);
          }
      });
    }
    else if (typeof(cond) == 'string') {
      // shortcut for class selectors on current element
      var matches = /\.(\w+)/.exec(cond);
      if(matches) {
        return this.filter(function() {
          return this.hasClassName(matches[1]);
        });
      }
      // TODO [kball] This should be a filter on the set of current elements,
      // instead of being a re-readed selector search.
      //
      var elems = Enable.getElementsBySelector(cond);
      var results = this.filter(function() {
        return (elems.indexOf(this) !== -1);
      });
      return(results);
    }
    return Enable(keepers);
};

//adds a class name to element
Enable.methods.addClass = function(newAdd) {
    this.each(function() {
        this.addClassName(newAdd);
    });
    return this;
};

//returns if element has class name
Enable.methods.hasClass = function(newAdd) {
    return this[0].hasClassName(newAdd);
};

//removes a class name from element
Enable.methods.removeClass = function(removal) {
    this.each(function() {
        this.removeClassName(removal);
    });
    return this;
};

// Grab all of the tagName elements within current context
Enable.getElements = function(context, tag) {
    tag = tag || '*';
    var found = [];

    for (var a = 0, len = context.length; con = context[a], a < len; a++) {
        var eles;
        if (tag == '*')
            eles = con.all ? con.all : con.getElementsByTagName("*");
        else
            eles = con.getElementsByTagName(tag);

        for (var b = 0, leng = eles.length; b < leng; b++)
            found.push(eles[b]);
    }
    return found;
};

/******************************************************************************\
    Enable.getElementsBySelector
    Finds elements in the dom via a css3 selector string.

    TODO: this code needs a complete rewrite!

*******************************************************************************/
Enable.getElementsBySelector = function(all_selectors,scope) {
    var base = scope || document.getRootElement();
    var selected = [];
    var selectors = all_selectors.replace(/\s*([^\w])\s*/g, "$1").split(",");

    //hacky facebook work-around for getAttribute
    var getAttribute = function(el, a) {
                if (el.getAttribute) {
                        return el.getAttribute(a);
                }
                else {
                        var val = false;
                        var attributes = Enable.accessors;
                        val = attributes[a] ? el[attributes[a][0]]() : false;
                        val = val === true ? a : val;
                        return val;
                }
    };

    COMMA: for (var i = 0, len1 = selectors.length; selector = selectors[i], i < len1; i++) {
        var context = [base];
        var inheriters = selector.split(" ");

        SPACE: for (var j = 0, len2 = inheriters.length; element = inheriters[j], j < len2; j++) {
            //This part is to make sure that it is not part of a CSS3 Selector
            var left_bracket = element.indexOf("[");
            var right_bracket = element.indexOf("]");
            var pos = element.indexOf("#");//ID
            if (pos + 1 && !(pos > left_bracket && pos < right_bracket)) {
                var parts = element.split("#");
                var tag = parts[0];
                var id = parts[1];
                var ele = document.getElementById(id);
                if (!ele || (tag && ele.nodeName.toLowerCase() != tag)) { //Specified element not found
                    continue COMMA;
                }
                context = [ele];
                continue SPACE;
            }
            pos = element.indexOf(".");//Class
            if (pos + 1 && !(pos > left_bracket && pos < right_bracket)) {
                var parts = element.split('.');
                var tag = parts[0];
                var class_name = parts[1];

                var found = Enable.getElements(context, tag);
                context = [];
                for (var l = 0, len = found.length; fnd = found[l], l < len; l++) {
                    if (fnd.hasClassName(class_name))
                        context.push(fnd);
                }
                continue SPACE;
            }
            //If the char '[' appears, that means it needs CSS 3 parsing
            if (element.indexOf('[') + 1) {
                var css3 = element.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?['"]?([^\]'"]*)['"]?\]$/);
                if (css3) {
                    var tag = css3[1];
                    var attr = css3[2];
                    var operator = css3[3];
                    var value = css3[4];
                }
                var found = Enable.getElements(context, tag);
                context = [];
                for (var l = 0, len = found.length; fnd = found[l], l < len; l++) {
                    fnd.getAttribute = function(a) {
                        return getAttribute(this, a);
                    };
                    if (operator == '=' && fnd.getAttribute(attr) != value)
                        continue;
                    if (operator == '~' && !fnd.getAttribute(attr).match(new RegExp('(^|\\s)' + value + '(\\s|$)')))
                        continue;
                    if (operator == '|' && !fnd.getAttribute(attr).match(new RegExp('^' + value + '-?')))
                        continue;
                    if (operator == '^' && fnd.getAttribute(attr).indexOf(value) != 0)
                        continue;
                    if (operator == '$' && fnd.getAttribute(attr).lastIndexOf(value) != (fnd.getAttribute(attr).length - value.length))
                        continue;
                    if (operator == '*' && !(fnd.getAttribute(attr).indexOf(value) + 1))
                        continue;
                    else if (!fnd.getAttribute(attr))
                        continue;
                    context.push(fnd);
                }
                continue SPACE;
            }
            //Tag selectors - no class or id specified.
            var found = Enable.getElements(context, element);
            context = found;
        }
        for (var o = 0, len = context.length; o < len; o++)
            selected.push(context[o]);
    }
    return selected;
};

/******************************************************************************\
Known Incompatabilities:
    Google Chrome (FBJS bug, Doesn't work at all? WTF)
    Internet Explorer (JS bug, replace() will only work on one element)
    Firefox (FBJS bug, top and left offset can be off by neighboring margins)

Dependancies:
    FBJS Library
    FBJS Animation
*******************************************************************************/

})();// end of enable.js
