/*
    Copyright (c) 2004-2007, The Dojo Foundation
    All Rights Reserved.

    Licensed under the Academic Free License version 2.1 or above OR the
    modified BSD license. For more information on Dojo licensing, see:

        http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
*/

/*
    This is a compiled version of Dojo, built for deployment and not for
    development. To get an editable version, please visit:

        http://dojotoolkit.org

    for documentation and information on getting the source.
*/

if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.fx"] = true;
dojo.provide("dojo.fx");
dojo.provide("dojo.fx.Toggler");

/*=====
dojo.fx = {
    // summary: Effects library on top of Base animations
};
=====*/

dojo.fx.chain = function(/*dojo._Animation[]*/ animations){
    // summary: Chain a list of dojo._Animation s to run in sequence
    // example:
    //    |    dojo.fx.chain([
    //    |        dojo.fadeIn({ node:node }),
    //    |        dojo.fadeOut({ node:otherNode })
    //    |    ]).play();
    //
    var first = animations.shift();
    var previous = first;
    dojo.forEach(animations, function(current){
        dojo.connect(previous, "onEnd", current, "play");
        previous = current;
    });
    return first; // dojo._Animation
};

dojo.fx.combine = function(/*dojo._Animation[]*/ animations){
    // summary: Combine a list of dojo._Animation s to run in parallel
    // example:
    //    |    dojo.fx.combine([
    //    |        dojo.fadeIn({ node:node }),
    //    |        dojo.fadeOut({ node:otherNode })
    //    |    ]).play();
    var ctr = new dojo._Animation({ curve: [0, 1] });
    if(!animations.length){ return ctr; }
    // animations.sort(function(a, b){ return a.duration-b.duration; });
    ctr.duration = animations[0].duration;
    dojo.forEach(animations, function(current){
        dojo.forEach([ "play", "pause", "stop" ],
            function(e){
                if(current[e]){
                    dojo.connect(ctr, e, current, e);
                }
            }
        );
    });
    return ctr; // dojo._Animation
};

dojo.declare("dojo.fx.Toggler", null, {
    // summary:
    //        class constructor for an animation toggler. It accepts a packed
    //        set of arguments about what type of animation to use in each
    //        direction, duration, etc.
    //
    // example:
    //    |    var t = new dojo.fx.Toggler({
    //    |        node: "nodeId",
    //    |        showDuration: 500,
    //    |        // hideDuration will default to "200"
    //    |        showFunc: dojo.wipeIn, 
    //    |        // hideFunc will default to "fadeOut"
    //    |    });
    //    |    t.show(100); // delay showing for 100ms
    //    |    // ...time passes...
    //    |    t.hide();

    // FIXME: need a policy for where the toggler should "be" the next
    // time show/hide are called if we're stopped somewhere in the
    // middle.

    constructor: function(args){
        var _t = this;

        dojo.mixin(_t, args);
        _t.node = args.node;
        _t._showArgs = dojo.mixin({}, args);
        _t._showArgs.node = _t.node;
        _t._showArgs.duration = _t.showDuration;
        _t.showAnim = _t.showFunc(_t._showArgs);

        _t._hideArgs = dojo.mixin({}, args);
        _t._hideArgs.node = _t.node;
        _t._hideArgs.duration = _t.hideDuration;
        _t.hideAnim = _t.hideFunc(_t._hideArgs);

        dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
        dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
    },

    // node: DomNode
    //    the node to toggle
    node: null,

    // showFunc: Function
    //    The function that returns the dojo._Animation to show the node
    showFunc: dojo.fadeIn,

    // hideFunc: Function    
    //    The function that returns the dojo._Animation to hide the node
    hideFunc: dojo.fadeOut,

    // showDuration:
    //    Time in milliseconds to run the show Animation
    showDuration: 200,

    // hideDuration:
    //    Time in milliseconds to run the hide Animation
    hideDuration: 200,

    /*=====
    _showArgs: null,
    _showAnim: null,

    _hideArgs: null,
    _hideAnim: null,

    _isShowing: false,
    _isHiding: false,
    =====*/

    show: function(delay){
        // summary: Toggle the node to showing
        return this.showAnim.play(delay || 0);
    },

    hide: function(delay){
        // summary: Toggle the node to hidden
        return this.hideAnim.play(delay || 0);
    }
});

dojo.fx.wipeIn = function(/*Object*/ args){
    // summary
    //        Returns an animation that will expand the
    //        node defined in 'args' object from it's current height to
    //        it's natural height (with no scrollbar).
    //        Node must have no margin/border/padding.
    args.node = dojo.byId(args.node);
    var node = args.node, s = node.style;

    var anim = dojo.animateProperty(dojo.mixin({
        properties: {
            height: {
                // wrapped in functions so we wait till the last second to query (in case value has changed)
                start: function(){
                    // start at current [computed] height, but use 1px rather than 0
                    // because 0 causes IE to display the whole panel
                    s.overflow="hidden";
                    if(s.visibility=="hidden"||s.display=="none"){
                        s.height="1px";
                        s.display="";
                        s.visibility="";
                        return 1;
                    }else{
                        var height = dojo.style(node, "height");
                        return Math.max(height, 1);
                    }
                },
                end: function(){
                    return node.scrollHeight;
                }
            }
        }
    }, args));

    dojo.connect(anim, "onEnd", function(){ 
        s.height = "auto";
    });

    return anim; // dojo._Animation
}

dojo.fx.wipeOut = function(/*Object*/ args){
    // summary
    //        Returns an animation that will shrink node defined in "args"
    //        from it's current height to 1px, and then hide it.
    var node = args.node = dojo.byId(args.node);
    var s = node.style;

    var anim = dojo.animateProperty(dojo.mixin({
        properties: {
            height: {
                end: 1 // 0 causes IE to display the whole panel
            }
        }
    }, args));

    dojo.connect(anim, "beforeBegin", function(){
        s.overflow = "hidden";
        s.display = "";
    });
    dojo.connect(anim, "onEnd", function(){
        s.height = "auto";
        s.display = "none";
    });

    return anim; // dojo._Animation
}

dojo.fx.slideTo = function(/*Object?*/ args){
    // summary
    //        Returns an animation that will slide "node" 
    //        defined in args Object from its current position to
    //        the position defined by (args.left, args.top).
    // example:
    //    |    dojo.fx.slideTo({ node: node, left:"40", top:"50", unit:"px" }).play()

    var node = (args.node = dojo.byId(args.node));
    
    var top = null;
    var left = null;
    
    var init = (function(n){
        return function(){
            var cs = dojo.getComputedStyle(n);
            var pos = cs.position;
            top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
            left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
            if(pos != 'absolute' && pos != 'relative'){
                var ret = dojo.coords(n, true);
                top = ret.y;
                left = ret.x;
                n.style.position="absolute";
                n.style.top=top+"px";
                n.style.left=left+"px";
            }
        };
    })(node);
    init();

    var anim = dojo.animateProperty(dojo.mixin({
        properties: {
            top: { end: args.top||0 },
            left: { end: args.left||0 }
        }
    }, args));
    dojo.connect(anim, "beforeBegin", anim, init);

    return anim; // dojo._Animation
}

}

if(!dojo._hasResource["dojox.fx.easing"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.fx.easing"] = true;
dojo.provide("dojox.fx.easing");
/*
    dojox.fx.easing is in this little file so you don't need dojox.fx to utilize this.
    dojox.fx has a lot of fun animations, but this module is optimized for size ... 

*/
dojox.fx.easing = {
    // summary: Collection of easing functions to use beyond the default dojo._defaultEasing
    // 
    // description:
    //    Easing functions are used to manipulate the iteration through
    //    an _Animation's _Line. _Line being the properties of an Animation,
    //    and the easing function progresses through that Line determing
    //    how quickly (or slowly) it should go. Or more accurately: modify
    //    the value of the _Line based on the percentage of animation completed.
    //    
    //    example:
    //    |    
    //    |    var anim = dojo.fadeOut({
    //    |        node: 'node',    
    //    |        duration: 2000,
    //    |        easing: dojox.fx.easing.quadIn
    //    |    }).play();
    //
    
    linear: function(/* Decimal? */n){
        // summary: A linear easing function
        return n;
    },

    quadIn: function(/* Decimal? */n){
        return Math.pow(n, 2);
    },

    quadOut: function(/* Decimal? */n){
        return n * (n-2) * -1;
    },

    quadInOut: function(/* Decimal? */n){
        n=n*2;
        if(n<1){ return Math.pow(n, 2) / 2; }
        return -1 * ((--n)*(n-2) - 1) / 2;
    },

    cubicIn: function(/* Decimal? */n){
        return Math.pow(n, 3);
    },

    cubicOut: function(/* Decimal? */n){
        return Math.pow(n-1, 3) + 1;
    },

    cubicInOut: function(/* Decimal? */n){
        n=n*2;
        if(n<1){ return Math.pow(n, 3) / 2; }
        n-=2;
        return (Math.pow(n, 3) + 2) / 2;
    },

    quartIn: function(/* Decimal? */n){
        return Math.pow(n, 4);
    },

    quartOut: function(/* Decimal? */n){
        return -1 * (Math.pow(n-1, 4) - 1);
    },

    quartInOut: function(/* Decimal? */n){
        n=n*2;
        if(n<1){ return Math.pow(n, 4) / 2; }
        n-=2;
        return -1/2 * (Math.pow(n, 4) - 2);
    },

    quintIn: function(/* Decimal? */n){
        return Math.pow(n, 5);
    },

    quintOut: function(/* Decimal? */n){
        return Math.pow(n-1, 5) + 1;
    },

    quintInOut: function(/* Decimal? */n){
        n=n*2;
        if(n<1){ return Math.pow(n, 5) / 2; };
        n-=2;
        return (Math.pow(n, 5) + 2) / 2;
    },

    sineIn: function(/* Decimal? */n){
        return -1 * Math.cos(n * (Math.PI/2)) + 1;
    },

    sineOut: function(/* Decimal? */n){
        return Math.sin(n * (Math.PI/2));
    },

    sineInOut: function(/* Decimal? */n){
        return -1 * (Math.cos(Math.PI*n) - 1) / 2;
    },

    expoIn: function(/* Decimal? */n){
        return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
    },

    expoOut: function(/* Decimal? */n){
        return (n==1) ? 1 : (-1 * Math.pow(2, -10 * n) + 1);
    },

    expoInOut: function(/* Decimal? */n){
        if(n==0){ return 0; }
        if(n==1){ return 1; }
        n = n*2;
        if(n<1){ return Math.pow(2, 10 * (n-1)) / 2; }
        --n;
        return (-1 * Math.pow(2, -10 * n) + 2) / 2;
    },

    circIn: function(/* Decimal? */n){
        return -1 * (Math.sqrt(1 - Math.pow(n, 2)) - 1);
    },

    circOut: function(/* Decimal? */n){
        n = n-1;
        return Math.sqrt(1 - Math.pow(n, 2));
    },

    circInOut: function(/* Decimal? */n){
        n = n*2;
        if(n<1){ return -1/2 * (Math.sqrt(1 - Math.pow(n, 2)) - 1); }
        n-=2;
        return 1/2 * (Math.sqrt(1 - Math.pow(n, 2)) + 1);
    },

    backIn: function(/* Decimal? */n){
        var s = 1.70158;
        return Math.pow(n, 2) * ((s+1)*n - s);
    },

    backOut: function(/* Decimal? */n){
        // summary: an easing function that pops past the range briefly, and 
        //     slowly comes back. 
        n = n - 1;
        var s = 1.70158;
        return Math.pow(n, 2) * ((s + 1) * n + s) + 1;
    },

    backInOut: function(/* Decimal? */n){
        var s = 1.70158 * 1.525;
        n = n*2;
        if(n < 1){ return (Math.pow(n, 2)*((s+1)*n - s))/2; }
        n-=2;
        return (Math.pow(n, 2)*((s+1)*n + s) + 2)/2;
    },

    elasticIn: function(/* Decimal? */n){
        if(n==0){ return 0; }
        if(n==1){ return 1; }
        var p = .3;
        var s = p/4;
        n = n - 1;
        return -1 * Math.pow(2,10*n) * Math.sin((n-s)*(2*Math.PI)/p);
    },

    elasticOut: function(/* Decimal? */n){
        // summary: An easing function that elasticly snaps around the target value, near the end of the Animation
        if(n==0) return 0;
        if(n==1) return 1;
        var p = .3;
        var s = p/4;
        return Math.pow(2,-10*n) * Math.sin((n-s)*(2*Math.PI)/p) + 1;
    },

    elasticInOut: function(/* Decimal? */n){
        // summary: An easing function that elasticly snaps around the value, near the beginning and end of the Animation        
        if(n==0) return 0;
        n = n*2;
        if(n==2) return 1;
        var p = .3*1.5;
        var s = p/4;
        if(n<1){
            n-=1;
            return -.5*(Math.pow(2,10*n) * Math.sin((n-s)*(2*Math.PI)/p));
        }
        n-=1;
        return .5*(Math.pow(2,-10*n) * Math.sin((n-s)*(2*Math.PI)/p)) + 1;
    },

    bounceIn: function(/* Decimal? */n){
        // summary: An easing function that "bounces" near the beginning of an Animation
        return (1 - dojox.fx.easing.bounceOut(1-n)); // Decimal
    },

    bounceOut: function(/* Decimal? */n){
        // summary: An easing function that "bounces" near the end of an Animation
        var s=7.5625;
        var p=2.75;
        var l; 
        if(n < (1 / p)){
            l = s*Math.pow(n, 2);
        }else if(n < (2 / p)){
            n -= (1.5 / p);
            l = s * Math.pow(n, 2) + .75;
        }else if(n < (2.5 / p)){
            n -= (2.25 / p);
            l = s * Math.pow(n, 2) + .9375;
        }else{
            n -= (2.625 / p);
            l = s * Math.pow(n, 2) + .984375;
        }
        return l;
    },

    bounceInOut: function(/* Decimal? */n){
        // summary: An easing function that "bounces" at the beginning and end of the Animation
        if(n<0.5){ return dojox.fx.easing.bounceIn(n*2) / 2; }
        return (dojox.fx.easing.bounceOut(n*2-1) / 2) + 0.5; // Decimal
    }
};

}

if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit._base.place"] = true;
dojo.provide("dijit._base.place");

// ported from dojo.html.util

dijit.getViewport = function(){
    //    summary
    //    Returns the dimensions and scroll position of the viewable area of a browser window

    var _window = dojo.global;
    var _document = dojo.doc;

    // get viewport size
    var w = 0, h = 0;
    var de = _document.documentElement;
    var dew = de.clientWidth, deh = de.clientHeight;
    if(dojo.isMozilla){
        // mozilla
        // _window.innerHeight includes the height taken by the scroll bar
        // clientHeight is ideal but has DTD issues:
        // #4539: FF reverses the roles of body.clientHeight/Width and documentElement.clientHeight/Width based on the DTD!
        // check DTD to see whether body or documentElement returns the viewport dimensions using this algorithm:
        var minw, minh, maxw, maxh;
        var dbw = _document.body.clientWidth;
        if(dbw > dew){
            minw = dew;
            maxw = dbw;
        }else{
            maxw = dew;
            minw = dbw;
        }
        var dbh = _document.body.clientHeight;
        if(dbh > deh){
            minh = deh;
            maxh = dbh;
        }else{
            maxh = deh;
            minh = dbh;
        }
        w = (maxw > _window.innerWidth) ? minw : maxw;
        h = (maxh > _window.innerHeight) ? minh : maxh;
    }else if(!dojo.isOpera && _window.innerWidth){
        //in opera9, dojo.body().clientWidth should be used, instead
        //of window.innerWidth/document.documentElement.clientWidth
        //so we have to check whether it is opera
        w = _window.innerWidth;
        h = _window.innerHeight;
    }else if(dojo.isIE && de && deh){
        w = dew;
        h = deh;
    }else if(dojo.body().clientWidth){
        // IE5, Opera
        w = dojo.body().clientWidth;
        h = dojo.body().clientHeight;
    }

    // get scroll position
    var scroll = dojo._docScroll();

    return { w: w, h: h, l: scroll.x, t: scroll.y };    //    object
};

dijit.placeOnScreen = function(
    /* DomNode */    node,
    /* Object */        pos,
    /* Object */        corners,
    /* boolean? */        tryOnly){
    //    summary:
    //        Keeps 'node' in the visible area of the screen while trying to
    //        place closest to pos.x, pos.y. The input coordinates are
    //        expected to be the desired document position.
    //
    //        Set which corner(s) you want to bind to, such as
    //        
    //            placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
    //        
    //        The desired x/y will be treated as the topleft(TL)/topright(TR) or
    //        BottomLeft(BL)/BottomRight(BR) corner of the node. Each corner is tested
    //        and if a perfect match is found, it will be used. Otherwise, it goes through
    //        all of the specified corners, and choose the most appropriate one.
    //        
    //        NOTE: node is assumed to be absolutely or relatively positioned.

    var choices = dojo.map(corners, function(corner){ return { corner: corner, pos: pos }; });

    return dijit._place(node, choices);
}

dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){
    // summary:
    //        Given a list of spots to put node, put it at the first spot where it fits,
    //        of if it doesn't fit anywhere then the place with the least overflow
    // choices: Array
    //        Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
    //        Above example says to put the top-left corner of the node at (10,20)
    //    layoutNode: Function(node, aroundNodeCorner, nodeCorner)
    //        for things like tooltip, they are displayed differently (and have different dimensions)
    //        based on their orientation relative to the parent.   This adjusts the popup based on orientation.

    // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
    // viewport over document
    var view = dijit.getViewport();

    // This won't work if the node is inside a <div style="position: relative">,
    // so reattach it to document.body.   (Otherwise, the positioning will be wrong
    // and also it might get cutoff)
    if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
        dojo.body().appendChild(node);
    }

    var best = null;
    dojo.some(choices, function(choice){
        var corner = choice.corner;
        var pos = choice.pos;

        // configure node to be displayed in given position relative to button
        // (need to do this in order to get an accurate size for the node, because
        // a tooltips size changes based on position, due to triangle)
        if(layoutNode){
            layoutNode(node, choice.aroundCorner, corner);
        }

        // get node's size
        var style = node.style;
        var oldDisplay = style.display;
        var oldVis = style.visibility;
        style.visibility = "hidden";
        style.display = "";
        var mb = dojo.marginBox(node);
        style.display = oldDisplay;
        style.visibility = oldVis;

        // coordinates and size of node with specified corner placed at pos,
        // and clipped by viewport
        var startX = (corner.charAt(1) == 'L' ? pos.x : Math.max(view.l, pos.x - mb.w)),
            startY = (corner.charAt(0) == 'T' ? pos.y : Math.max(view.t, pos.y -  mb.h)),
            endX = (corner.charAt(1) == 'L' ? Math.min(view.l + view.w, startX + mb.w) : pos.x),
            endY = (corner.charAt(0) == 'T' ? Math.min(view.t + view.h, startY + mb.h) : pos.y),
            width = endX - startX,
            height = endY - startY,
            overflow = (mb.w - width) + (mb.h - height);

        if(best == null || overflow < best.overflow){
            best = {
                corner: corner,
                aroundCorner: choice.aroundCorner,
                x: startX,
                y: startY,
                w: width,
                h: height,
                overflow: overflow
            };
        }
        return !overflow;
    });

    node.style.left = best.x + "px";
    node.style.top = best.y + "px";
    return best;
}

dijit.placeOnScreenAroundElement = function(
    /* DomNode */        node,
    /* DomNode */        aroundNode,
    /* Object */        aroundCorners,
    /* Function */        layoutNode){

    //    summary
    //    Like placeOnScreen, except it accepts aroundNode instead of x,y
    //    and attempts to place node around it.  Uses margin box dimensions.
    //
    //    aroundCorners
    //        specify Which corner of aroundNode should be
    //        used to place the node => which corner(s) of node to use (see the
    //        corners parameter in dijit.placeOnScreen)
    //        e.g. {'TL': 'BL', 'BL': 'TL'}
    //
    //    layoutNode: Function(node, aroundNodeCorner, nodeCorner)
    //        for things like tooltip, they are displayed differently (and have different dimensions)
    //        based on their orientation relative to the parent.   This adjusts the popup based on orientation.


    // get coordinates of aroundNode
    aroundNode = dojo.byId(aroundNode);
    var oldDisplay = aroundNode.style.display;
    aroundNode.style.display="";
    // #3172: use the slightly tighter border box instead of marginBox
    var aroundNodeW = aroundNode.offsetWidth; //mb.w;
    var aroundNodeH = aroundNode.offsetHeight; //mb.h;
    var aroundNodePos = dojo.coords(aroundNode, true);
    aroundNode.style.display=oldDisplay;

    // Generate list of possible positions for node
    var choices = [];
    for(var nodeCorner in aroundCorners){
        choices.push( {
            aroundCorner: nodeCorner,
            corner: aroundCorners[nodeCorner],
            pos: {
                x: aroundNodePos.x + (nodeCorner.charAt(1) == 'L' ? 0 : aroundNodeW),
                y: aroundNodePos.y + (nodeCorner.charAt(0) == 'T' ? 0 : aroundNodeH)
            }
        });
    }

    return dijit._place(node, choices, layoutNode);
}

}

if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.common"] = true;
dojo.provide("dojo.dnd.common");

dojo.dnd._copyKey = navigator.appVersion.indexOf("Macintosh") < 0 ? "ctrlKey" : "metaKey";

dojo.dnd.getCopyKeyState = function(e) {
    // summary: abstracts away the difference between selection on Mac and PC,
    //    and returns the state of the "copy" key to be pressed.
    // e: Event: mouse event
    return e[dojo.dnd._copyKey];    // Boolean
};

dojo.dnd._uniqueId = 0;
dojo.dnd.getUniqueId = function(){
    // summary: returns a unique string for use with any DOM element
    var id;
    do{
        id = "dojoUnique" + (++dojo.dnd._uniqueId);
    }while(dojo.byId(id));
    return id;
};

dojo.dnd._empty = {};

dojo.dnd.isFormElement = function(/*Event*/ e){
    // summary: returns true, if user clicked on a form element
    var t = e.target;
    if(t.nodeType == 3 /*TEXT_NODE*/){
        t = t.parentNode;
    }
    return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;    // Boolean
};

}

if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.autoscroll"] = true;
dojo.provide("dojo.dnd.autoscroll");

dojo.dnd.getViewport = function(){
    // summary: returns a viewport size (visible part of the window)

    // FIXME: need more docs!!
    var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
    if(dojo.isMozilla){
        return {w: dd.clientWidth, h: w.innerHeight};    // Object
    }else if(!dojo.isOpera && w.innerWidth){
        return {w: w.innerWidth, h: w.innerHeight};        // Object
    }else if (!dojo.isOpera && dd && dd.clientWidth){
        return {w: dd.clientWidth, h: dd.clientHeight};    // Object
    }else if (b.clientWidth){
        return {w: b.clientWidth, h: b.clientHeight};    // Object
    }
    return null;    // Object
};

dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;

dojo.dnd.V_AUTOSCROLL_VALUE = 16;
dojo.dnd.H_AUTOSCROLL_VALUE = 16;

dojo.dnd.autoScroll = function(e){
    // summary:
    //        a handler for onmousemove event, which scrolls the window, if
    //        necesary
    // e: Event:
    //        onmousemove event

    // FIXME: needs more docs!
    var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
    if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
        dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
    }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
        dx = dojo.dnd.H_AUTOSCROLL_VALUE;
    }
    if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
        dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
    }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
        dy = dojo.dnd.V_AUTOSCROLL_VALUE;
    }
    window.scrollBy(dx, dy);
};

dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};

dojo.dnd.autoScrollNodes = function(e){
    // summary:
    //        a handler for onmousemove event, which scrolls the first avaialble
    //        Dom element, it falls back to dojo.dnd.autoScroll()
    // e: Event:
    //        onmousemove event

    // FIXME: needs more docs!
    for(var n = e.target; n;){
        if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
            var s = dojo.getComputedStyle(n);
            if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
                var b = dojo._getContentBox(n, s), t = dojo._abs(n, true);
                // console.debug(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
                b.l += t.x + n.scrollLeft;
                b.t += t.y + n.scrollTop;
                var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2), 
                    h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
                    rx = e.pageX - b.l, ry = e.pageY - b.t, dx = 0, dy = 0;
                if(rx > 0 && rx < b.w){
                    if(rx < w){
                        dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
                    }else if(rx > b.w - w){
                        dx = dojo.dnd.H_AUTOSCROLL_VALUE;
                    }
                }
                //console.debug("ry =", ry, "b.h =", b.h, "h =", h);
                if(ry > 0 && ry < b.h){
                    if(ry < h){
                        dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
                    }else if(ry > b.h - h){
                        dy = dojo.dnd.V_AUTOSCROLL_VALUE;
                    }
                }
                var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
                n.scrollLeft = n.scrollLeft + dx;
                n.scrollTop  = n.scrollTop  + dy;
                // if(dx || dy){ console.debug(oldLeft + ", " + oldTop + "\n" + dx + ", " + dy + "\n" + n.scrollLeft + ", " + n.scrollTop); }
                if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
            }
        }
        try{
            n = n.parentNode;
        }catch(x){
            n = null;
        }
    }
    dojo.dnd.autoScroll(e);
};

}

if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Mover"] = true;
dojo.provide("dojo.dnd.Mover");




dojo.declare("dojo.dnd.Mover", null, {
    constructor: function(node, e, host){
        // summary: an object, which makes a node follow the mouse, 
        //    used as a default mover, and as a base class for custom movers
        // node: Node: a node (or node's id) to be moved
        // e: Event: a mouse event, which started the move;
        //    only pageX and pageY properties are used
        // host: Object?: object which implements the functionality of the move,
        //     and defines proper events (onMoveStart and onMoveStop)
        this.node = dojo.byId(node);
        this.marginBox = {l: e.pageX, t: e.pageY};
        this.mouseButton = e.button;
        var h = this.host = host, d = node.ownerDocument, 
            firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
        this.events = [
            dojo.connect(d, "onmousemove", this, "onMouseMove"),
            dojo.connect(d, "onmouseup",   this, "onMouseUp"),
            // cancel text selection and text dragging
            dojo.connect(d, "ondragstart",   dojo, "stopEvent"),
            dojo.connect(d, "onselectstart", dojo, "stopEvent"),
            firstEvent
        ];
        // notify that the move has started
        if(h && h.onMoveStart){
            h.onMoveStart(this);
        }
    },
    // mouse event processors
    onMouseMove: function(e){
        // summary: event processor for onmousemove
        // e: Event: mouse event
        dojo.dnd.autoScroll(e);
        var m = this.marginBox;
        this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY});
    },
    onMouseUp: function(e){
        if(this.mouseButton == e.button){
            this.destroy();
        }
    },
    // utilities
    onFirstMove: function(){
        // summary: makes the node absolute; it is meant to be called only once
        this.node.style.position = "absolute";    // enforcing the absolute mode
        var m = dojo.marginBox(this.node);
        m.l -= this.marginBox.l;
        m.t -= this.marginBox.t;
        this.marginBox = m;
        this.host.onFirstMove(this);
        dojo.disconnect(this.events.pop());
    },
    destroy: function(){
        // summary: stops the move, deletes all references, so the object can be garbage-collected
        dojo.forEach(this.events, dojo.disconnect);
        // undo global settings
        var h = this.host;
        if(h && h.onMoveStop){
            h.onMoveStop(this);
        }
        // destroy objects
        this.events = this.node = null;
    }
});

}

if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Moveable"] = true;
dojo.provide("dojo.dnd.Moveable");



dojo.declare("dojo.dnd.Moveable", null, {
    // object attributes (for markup)
    handle: "",
    delay: 0,
    skip: false,
    
    constructor: function(node, params){
        // summary: an object, which makes a node moveable
        // node: Node: a node (or node's id) to be moved
        // params: Object: an optional object with additional parameters;
        //    following parameters are recognized:
        //        handle: Node: a node (or node's id), which is used as a mouse handle
        //            if omitted, the node itself is used as a handle
        //        delay: Number: delay move by this number of pixels
        //        skip: Boolean: skip move of form elements
        //        mover: Object: a constructor of custom Mover
        this.node = dojo.byId(node);
        if(!params){ params = {}; }
        this.handle = params.handle ? dojo.byId(params.handle) : null;
        if(!this.handle){ this.handle = this.node; }
        this.delay = params.delay > 0 ? params.delay : 0;
        this.skip  = params.skip;
        this.mover = params.mover ? params.mover : dojo.dnd.Mover;
        this.events = [
            dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
            // cancel text selection and text dragging
            dojo.connect(this.handle, "ondragstart",   this, "onSelectStart"),
            dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
        ];
    },

    // markup methods
    markupFactory: function(params, node){
        return new dojo.dnd.Moveable(node, params);
    },

    // methods
    destroy: function(){
        // summary: stops watching for possible move, deletes all references, so the object can be garbage-collected
        dojo.forEach(this.events, dojo.disconnect);
        this.events = this.node = this.handle = null;
    },
    
    // mouse event processors
    onMouseDown: function(e){
        // summary: event processor for onmousedown, creates a Mover for the node
        // e: Event: mouse event
        if(this.skip && dojo.dnd.isFormElement(e)){ return; }
        if(this.delay){
            this.events.push(dojo.connect(this.handle, "onmousemove", this, "onMouseMove"));
            this.events.push(dojo.connect(this.handle, "onmouseup", this, "onMouseUp"));
            this._lastX = e.pageX;
            this._lastY = e.pageY;
        }else{
            new this.mover(this.node, e, this);
        }
        dojo.stopEvent(e);
    },
    onMouseMove: function(e){
        // summary: event processor for onmousemove, used only for delayed drags
        // e: Event: mouse event
        if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
            this.onMouseUp(e);
            new this.mover(this.node, e, this);
        }
        dojo.stopEvent(e);
    },
    onMouseUp: function(e){
        // summary: event processor for onmouseup, used only for delayed delayed drags
        // e: Event: mouse event
        dojo.disconnect(this.events.pop());
        dojo.disconnect(this.events.pop());
    },
    onSelectStart: function(e){
        // summary: event processor for onselectevent and ondragevent
        // e: Event: mouse event
        if(!this.skip || !dojo.dnd.isFormElement(e)){
            dojo.stopEvent(e);
        }
    },
    
    // local events
    onMoveStart: function(/* dojo.dnd.Mover */ mover){
        // summary: called before every move operation
        dojo.publish("/dnd/move/start", [mover]);
        dojo.addClass(dojo.body(), "dojoMove"); 
        dojo.addClass(this.node, "dojoMoveItem"); 
    },
    onMoveStop: function(/* dojo.dnd.Mover */ mover){
        // summary: called after every move operation
        dojo.publish("/dnd/move/stop", [mover]);
        dojo.removeClass(dojo.body(), "dojoMove");
        dojo.removeClass(this.node, "dojoMoveItem");
    },
    onFirstMove: function(/* dojo.dnd.Mover */ mover){
        // summary: called during the very first move notification,
        //    can be used to initialize coordinates, can be overwritten.
        
        // default implementation does nothing
    },
    onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
        // summary: called during every move notification,
        //    should actually move the node, can be overwritten.
        this.onMoving(mover, leftTop);
        dojo.marginBox(mover.node, leftTop);
        this.onMoved(mover, leftTop);
    },
    onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
        // summary: called before every incremental move,
        //    can be overwritten.
        
        // default implementation does nothing
    },
    onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
        // summary: called after every incremental move,
        //    can be overwritten.
        
        // default implementation does nothing
    }
});

}

if(!dojo._hasResource["mojo.roller"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["mojo.roller"] = true;
dojo.provide("mojo.roller");
// summary: takes a <ul id="whyList"> and turns the <li>'s into items to rotate
//             in a fade sequence. you can set mojo.roller.delay at any time, or
//             modify mojo.roller.items array using any native Array techniques
//            at any time.
(function(){
    
    dojo.mixin(mojo.roller,{
        
        // the time to wait before rolling to next item
        delay: 2000,
        
        // the items in the roller are just an strings in an array:
        items: [],
        
        // these are "private", and don't need to be accessed by a user:
        _roller: null, // the pointer to the roller node (a <p>) element)
        _idx: 0, // the current index of the visible roller item
        _anim: {}, // a placeholder for our transition animations
        
        start: function(){
            // summary: start the rollout sequent, and create a roller if one
            //             doesn't already exist.
            if(!mojo.roller._roller){ mojo.roller.init(); }
            mojo.roller._anim.fadeOut.gotoPercent(0,true);
        },
        
        stop: function(){
            // summary: stop (potentially) both animations
            var m = mojo.roller._anim;
            m.fadeIn.stop();
            m.fadeOut.stop();
        },
        
        _set: function(/* Int */n){
            // summary: set the roller to a specified offset
            if(n<0){ n = mojo.roller.items.length-1; }
            if(n>mojo.roller.items.length-1){ n = 0; }
            mojo.roller._roller.innerHTML = mojo.roller.items[n] || "error ...";
            mojo.roller._idx = n;
        },

        init: function(){
            // summary: create and setup our roller and connections
            
            var r = mojo.roller._roller = dojo.byId("whyDojoRoll");
            
            // static animations:
            dojo.mixin(mojo.roller._anim,{
                fadeOut: dojo.fadeOut({ duration:400, node: r }),
                fadeIn: dojo.fadeIn({ duration:275, node: r })
            });
            
            // after fadeOut, set to next roller item
            dojo.connect(mojo.roller._anim.fadeOut,"onEnd",function(){
                mojo.roller._set(mojo.roller._idx+1);
                mojo.roller._anim.fadeIn.play(15); // and fade back in
            });
            
            // after fadeIn, recycle animation, and do it again after a delay
            dojo.connect(mojo.roller._anim.fadeIn,"onEnd",function(){
                setTimeout(dojo.hitch(mojo.roller,"start"),mojo.roller.delay);
            });
            
            // turn each LI element in #whyList into a roller item, and destroy
            // the element:
            dojo.query("li","whyList").forEach(function(item){
                mojo.roller.items.push(item.innerHTML);
                dojo._destroyElement(item);
            });

        }

    });
    
})();

}

if(!dojo._hasResource["mojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["mojo._base"] = true;
dojo.provide("mojo._base");

// our custom namespace core rollup file:



 function strlen( string ){
  
    return ("" + string).length;
}

(function(){ 
        
    var nodes;
    dojo.addOnLoad(function(){
        
        nodes = dojo.query("#container > div");
        // iterate over each div in the container
        nodes.forEach(function(n){
            // hide the node, first thing, and undo native-css hiding:
            dojo.style(n,"opacity","0");
            dojo.style(n,"visibility","visible");

            // the drag handle will be the h1 element in this div            
            var handle = dojo.query("h2",n)[0];
            new dojo.dnd.Moveable(n,{ handle: handle });

            // there is really only one image in here though:
            dojo.query("img",n).forEach(function(img){
                with(img.style){
                    width = "1px"; // 0 is bad
                    height = "1px"; 
                    top = "113px"; // 310/2
                    left = "123px"; 
                }
                if(dojo.isIE){
                    // no png's for ie users
                    img.src=img.src.substr(0, (strlen(img.src)-3))+"gif";
                    //alert(img.src);
                }
            });
        });
        
        // dojo.fx.combine takes an array of animations:
        var _anims = [];
        var _delay = 100;
        
        nodes.forEach(function(n){
            // fade in the node, delayed 500ms
            _anims.push(dojo.fadeIn({
                duration:1,
                node: n, delay: _delay + 200,
                properties: {
                    paddingTop: {
                        start:155, end:1, unit:"px"
                    },
                    fontSize:{
                        start:0.1, end:16, unit:"px"
                    }
                }
            }))
            
            dojo.query("img",n).forEach(function(img){
                _anims.push(dojo.animateProperty({
                    duration:1,
                    delay:_delay + 100,
                    node:img,
                    properties: {
                        width: { end:227, unit:"px" },
                        height: { end:246, unit:"px" },
                        top: { end:0 }, left: { end: 0 }
                    }
                }));
            });

            _delay += 150; // step up the delay base just a smidge

        });
        
        // add the header-in-animation to our _anims array
        _anims.push(dojo.animateProperty({
            node: "header",
            properties: {
                top: { end: 5, unit:"px" },
                left: { end: 5, unit:"px" }
            },
            delay: _delay,
            duration: 700
        }));
        
        // combine them all, and play a s single animation (with a
        // setTimeout to give the broswer a second to be sane again)
        var anim = dojo.fx.combine(_anims);
        dojo.connect(anim,"onEnd",mojo.roller,"start");
        setTimeout(dojo.hitch(anim,"play"),5);
        
        var _coords = null;
        var _z = null;
        
        dojo.subscribe("/dnd/move/start",function(e){
            // when drag starts, save the coords of the node we're pulling
            var n = e.node;
            _coords = dojo.coords(n);
            // and "bring to top"
            _z = dojo.style(n,"zIndex");
            dojo.style(n,"zIndex","888");
            // and make it partially opaque
            dojo.style(n,"opacity","0.65");
        });
        
        dojo.subscribe("/dnd/move/stop",function(e){
            // when it ends, reset z-index, opacity, and animate back to spot
            dojo.style(e.node,"opacity","1");
            if(_coords){
                dojo.fx.slideTo({
                    node: e.node, // drag node
                    top: _coords.t, // tmp top
                    left: _coords.l, // tmp left
                    easing: dojox.fx.easing.elasticOut,
                    duration:950 // ms
                }).play(5); // small delay for performance?
                dojo.style(e.node,"zIndex",_z);
            }
        });    

    });

})();

}

if(!dojo._hasResource["mojo.drop"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["mojo.drop"] = true;
dojo.provide("mojo.drop");
// adds gravity effect to mojoDemo



(function(){ 

    // global here:
    var nodes;
    var _coords = [];
    var cb = null;
    
    mojo.drop._calcPositions = function(e){
        // store or return the current calculated positions of each ball
        var c = [];
        nodes.forEach(function(n){
            c.push(dojo.coords(n));
        });
        
        if(e){
            _coords = c; // with luck maybe in this limited scope, but this is bad.
        }
        return c; // Array
    };
    
    mojo.drop.dropNodes = function(){
        // summary: drop all the nodes using the bounce easing function

        _coords = mojo.drop._calcPositions(); // store positions for later
        // ball is 310px, so the bottom edges are height - 310 roughly.
        var t = dijit.getViewport().h - 227; 
        dojo.style(dojo.body(),"overflow","hidden");
    
        var _anims = [];
        nodes.forEach(function(n,idx){
            // we want to keep the left: attribute in tact, so pass it along
            var l = _coords[idx].l;
            _anims.push(dojo.fx.slideTo({
                top:t, left:l, node:n,
                easing:dojox.fx.easing.bounceOut
            }));
        });
        // play the _anims as one animation
        dojo.fx.combine(_anims).play();
    }
    
    mojo.drop.floatNodes = function(){
        // summary: reset all the nodes to the orig. positions
        var _anims = [];
        nodes.forEach(function(n,idx){
            // push each slide animation in _anims, based on it's stored coords
            var t = _coords[idx].t;
            var l = _coords[idx].l;
            _anims.push(dojo.fx.slideTo({
                top: t, left:l, node:n
            }));            
        });
        // play the anim, and set overflow:auto on body
        var _anim = dojo.fx.combine(_anims);
        var con = dojo.connect(_anim,"onEnd",function(){
            dojo.style(dojo.body(),"overflow","visible"); 
        });
        _anim.play();
    }
    
    var _toggleGravity = function(e){
        // drop or float the nodes based on the state of the checkbox
        mojo.drop[(cb.checked ? "dropNodes" : "floatNodes")]();
    };
    
    dojo.addOnLoad(function(){
        // convenience:
        nodes = dojo.query("#container > div");

        // setup the "gravity toggler"
        cb = dojo.byId("gravity");
        cb.checked = false;
        // FIXME: ie7 fires onchange after blur() ... ugh
        dojo.connect(cb,"onchange",_toggleGravity);

        // just in case, because our nodes are absolutely positioned:
        dojo.connect(window,"onresize",mojo.drop,"_calcPositions");

        // lets make the logo drag/snap-able, too.
        new dojo.dnd.Moveable("logoImg");
        dojo.style("logoImg","cursor","move");
    });

})();

}

if(!dojo._hasResource["dojo.io.iframe"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.io.iframe"] = true;
dojo.provide("dojo.io.iframe");

dojo.io.iframe = {
    create: function(/*String*/fname, /*String*/onloadstr, /*String?*/uri){
        //    summary:
        //        Creates a hidden iframe in the page. Used mostly for IO
        //        transports.  You do not need to call this to start a
        //        dojo.io.iframe request. Just call send().
        //    fname: String
        //        The name of the iframe. Used for the name attribute on the
        //        iframe.
        //    onloadstr: String
        //        A string of JavaScript that will be executed when the content
        //        in the iframe loads.
        //    uri: String
        //        The value of the src attribute on the iframe element. If a
        //        value is not given, then dojo/resources/blank.html will be
        //        used.
        if(window[fname]){ return window[fname]; }
        if(window.frames[fname]){ return window.frames[fname]; }
        var cframe = null;
        var turi = uri;
        if(!turi){
            if(djConfig["useXDomain"] && !djConfig["dojoBlankHtmlUrl"]){
                console.debug("dojo.io.iframe.create: When using cross-domain Dojo builds,"
                    + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl"
                    + " to the path on your domain to blank.html");
            }
            turi = (djConfig["dojoBlankHtmlUrl"]||dojo.moduleUrl("dojo", "resources/blank.html"));
        }
        var ifrstr = dojo.isIE ? '<iframe name="'+fname+'" src="'+turi+'" onload="'+onloadstr+'">' : 'iframe';
        cframe = dojo.doc.createElement(ifrstr);
        with(cframe){
            name = fname;
            setAttribute("name", fname);
            id = fname;
        }
        dojo.body().appendChild(cframe);
        window[fname] = cframe;
    
        with(cframe.style){
            if(dojo.isSafari < 3){
                //We can't change the src in Safari 2.0.3 if absolute position. Bizarro.
                position = "absolute";
            }
            left = top = "1px";
            height = width = "1px";
            visibility = "hidden";
        }

        if(!dojo.isIE){
            this.setSrc(cframe, turi, true);
            cframe.onload = new Function(onloadstr);
        }

        return cframe;
    },

    setSrc: function(/*DOMNode*/iframe, /*String*/src, /*Boolean*/replace){
        //summary:
        //        Sets the URL that is loaded in an IFrame. The replace parameter
        //        indicates whether location.replace() should be used when
        //        changing the location of the iframe.
        try{
            if(!replace){
                if(dojo.isSafari){
                    iframe.location = src;
                }else{
                    frames[iframe.name].location = src;
                }
            }else{
                // Fun with DOM 0 incompatibilities!
                var idoc;
                if(dojo.isIE || dojo.isSafari > 2){
                    idoc = iframe.contentWindow.document;
                }else if(dojo.isSafari){
                    idoc = iframe.document;
                }else{ //  if(d.isMozilla){
                    idoc = iframe.contentWindow;
                }
    
                //For Safari (at least 2.0.3) and Opera, if the iframe
                //has just been created but it doesn't have content
                //yet, then iframe.document may be null. In that case,
                //use iframe.location and return.
                if(!idoc){
                    iframe.location = src;
                    return;
                }else{
                    idoc.location.replace(src);
                }
            }
        }catch(e){ 
            console.debug("dojo.io.iframe.setSrc: ", e); 
        }
    },

    doc: function(/*DOMNode*/iframeNode){
        //summary: Returns the document object associated with the iframe DOM Node argument.
        var doc = iframeNode.contentDocument || // W3
            (
                (iframeNode.contentWindow)&&(iframeNode.contentWindow.document)
            ) ||  // IE
            (
                (iframeNode.name)&&(document.frames[iframeNode.name])&&
                (document.frames[iframeNode.name].document)
            ) || null;
        return doc;
    },

    /*=====
    dojo.io.iframe.__ioArgs = function(kwArgs){
        //    summary:
        //        All the properties described in the dojo.__ioArgs type, apply
        //        to this type. The following additional properties are allowed
        //        for dojo.io.iframe.send():
        //    method: String?
        //        The HTTP method to use. "GET" or "POST" are the only supported
        //        values.  It will try to read the value from the form node's
        //        method, then try this argument. If neither one exists, then it
        //        defaults to POST.
        //    handleAs: String?
        //        Specifies what format the result data should be given to the
        //        load/handle callback. Valid values are: text, html, javascript,
        //        json. IMPORTANT: For all values EXCEPT html, The server
        //        response should be an HTML file with a textarea element. The
        //        response data should be inside the textarea element. Using an
        //        HTML document the only reliable, cross-browser way this
        //        transport can know when the response has loaded. For the html
        //        handleAs value, just return a normal HTML document.  NOTE: xml
        //        or any other XML type is NOT supported by this transport.
        //    content: Object?
        //        If "form" is one of the other args properties, then the content
        //        object properties become hidden form form elements. For
        //        instance, a content object of {name1 : "value1"} is converted
        //        to a hidden form element with a name of "name1" and a value of
        //        "value1". If there is not a "form" property, then the content
        //        object is converted into a name=value&name=value string, by
        //        using dojo.objectToQuery().
    }
    =====*/

    send: function(/*dojo.io.iframe.__ioArgs*/args){
        //summary: function that sends the request to the server.
        //This transport can only process one send() request at a time, so if send() is called
        //multiple times, it will queue up the calls and only process one at a time.
        if(!this["_frame"]){
            this._frame = this.create(this._iframeName, "dojo.io.iframe._iframeOnload();");
        }

        //Set up the deferred.
        var dfd = dojo._ioSetArgs(
            args,
            function(/*Deferred*/dfd){
                //summary: canceller function for dojo._ioSetArgs call.
                dfd.canceled = true;
                dfd.ioArgs._callNext();
            },
            function(/*Deferred*/dfd){
                //summary: okHandler function for dojo._ioSetArgs call.
                var value = null;
                try{
                    var ioArgs = dfd.ioArgs;
                    var dii = dojo.io.iframe;
                    var ifd = dii.doc(dii._frame);
                    var handleAs = ioArgs.handleAs;

                    //Assign correct value based on handleAs value.
                    value = ifd; //html
                    if(handleAs != "html"){
                        value = ifd.getElementsByTagName("textarea")[0].value; //text
                        if(handleAs == "json"){
                            value = dojo.fromJson(value); //json
                        }else if(handleAs == "javascript"){
                            value = dojo.eval(value); //javascript
                        }
                    }
                }catch(e){
                    value = e;
                }finally{
                    ioArgs._callNext();                
                }
                return value;
            },
            function(/*Error*/error, /*Deferred*/dfd){
                //summary: errHandler function for dojo._ioSetArgs call.
                dfd.ioArgs._hasError = true;
                dfd.ioArgs._callNext();
                return error;
            }
        );

        //Set up a function that will fire the next iframe request. Make sure it only
        //happens once per deferred.
        dfd.ioArgs._callNext = function(){
            if(!this["_calledNext"]){
                this._calledNext = true;
                dojo.io.iframe._currentDfd = null;
                dojo.io.iframe._fireNextRequest();
            }
        }

        this._dfdQueue.push(dfd);
        this._fireNextRequest();
        
        //Add it the IO watch queue, to get things like timeout support.
        dojo._ioWatch(
            dfd,
            function(/*Deferred*/dfd){
                //validCheck
                return !dfd.ioArgs["_hasError"];
            },
            function(dfd){
                //ioCheck
                return (!!dfd.ioArgs["_finished"]);
            },
            function(dfd){
                //resHandle
                if(dfd.ioArgs._finished){
                    dfd.callback(dfd);
                }else{
                    dfd.errback(new Error("Invalid dojo.io.iframe request state"));
                }
            }
        );

        return dfd;
    },

    _currentDfd: null,
    _dfdQueue: [],
    _iframeName: "dojoIoIframe",

    _fireNextRequest: function(){
        //summary: Internal method used to fire the next request in the bind queue.
        try{
            if((this._currentDfd)||(this._dfdQueue.length == 0)){ return; }
            var dfd = this._currentDfd = this._dfdQueue.shift();
            var ioArgs = dfd.ioArgs;
            var args = ioArgs.args;

            ioArgs._contentToClean = [];
            var fn = args["form"];
            var content = args["content"] || {};
            if(fn){
                if(content){
                    // if we have things in content, we need to add them to the form
                    // before submission
                    for(var x in content){
                        if(!fn[x]){
                            var tn;
                            if(dojo.isIE){
                                tn = dojo.doc.createElement("<input type='hidden' name='"+x+"'>");
                            }else{
                                tn = dojo.doc.createElement("input");
                                tn.type = "hidden";
                                tn.name = x;
                            }
                            tn.value = content[x];
                            fn.appendChild(tn);
                            ioArgs._contentToClean.push(x);
                        }else{
                            fn[x].value = content[x];
                        }
                    }
                }
                //IE requires going through getAttributeNode instead of just getAttribute in some form cases, 
                //so use it for all.  See #2844
                var actnNode = fn.getAttributeNode("action");
                var mthdNode = fn.getAttributeNode("method");
                var trgtNode = fn.getAttributeNode("target");
                if(args["url"]){
                    ioArgs._originalAction = actnNode ? actnNode.value : null;
                    if(actnNode){
                        actnNode.value = args.url;
                    }else{
                        fn.setAttribute("action",args.url);
                    }
                }
                if(!mthdNode || !mthdNode.value){
                    if(mthdNode){
                        mthdNode.value= (args["method"]) ? args["method"] : "post";
                    }else{
                        fn.setAttribute("method", (args["method"]) ? args["method"] : "post");
                    }
                }
                ioArgs._originalTarget = trgtNode ? trgtNode.value: null;
                if(trgtNode){
                    trgtNode.value = this._iframeName;
                }else{
                    fn.setAttribute("target", this._iframeName);
                }
                fn.target = this._iframeName;
                fn.submit();
            }else{
                // otherwise we post a GET string by changing URL location for the
                // iframe
                var tmpUrl = args.url + (args.url.indexOf("?") > -1 ? "&" : "?") + ioArgs.query;
                this.setSrc(this._frame, tmpUrl, true);
            }
        }catch(e){
            dfd.errback(e);
        }
    },

    _iframeOnload: function(){
        var dfd = this._currentDfd;
        if(!dfd){
            this._fireNextRequest();
            return;
        }

        var ioArgs = dfd.ioArgs;
        var args = ioArgs.args;
        var fNode = args.form;
    
        if(fNode){
            // remove all the hidden content inputs
            var toClean = ioArgs._contentToClean;
            for(var i = 0; i < toClean.length; i++) {
                var key = toClean[i];
                if(dojo.isSafari < 3){
                    //In Safari (at least 2.0.3), can't use form[key] syntax to find the node,
                    //for nodes that were dynamically added.
                    for(var j = 0; j < fNode.childNodes.length; j++){
                        var chNode = fNode.childNodes[j];
                        if(chNode.name == key){
                            dojo._destroyElement(chNode);
                            break;
                        }
                    }
                }else{
                    dojo._destroyElement(fNode[key]);
                    fNode[key] = null;
                }
            }
    
            // restore original action + target
            if(ioArgs["_originalAction"]){
                fNode.setAttribute("action", ioArgs._originalAction);
            }
            if(ioArgs["_originalTarget"]){
                fNode.setAttribute("target", ioArgs._originalTarget);
                fNode.target = ioArgs._originalTarget;
            }
        }

        ioArgs._finished = true;
    }
}

}

