if ( typeof mayaFish.modules.dbForm == "undefined" )
    /**
     * dbForm
     *
     * @author David Pozsar <david.pozsar@megaweb.hu>
     */
	mayaFish.modules.dbForm = new ( function () {
        var checks = {};
        
        this.submit = function ( formId )
        {
            if ( formId in checks )
            {
                var success = true;
                
                mayaFish.forEach( checks[formId], function ( key, check ) {
                    if ( ! check() )
                    {
                        success = false;
                        return mayaFish.BREAK;
                    }
                } );
                
                return success;
            }
            
            return true;
        };
        
        this.visible = function ( element )
        {
            if ( element === window.document )
            {
                return true;
            }
            
            if ( element.style.display == "none" ||
                 element.style.visibility == "hidden" )
            {
                return false;
            }
            
            return this.visible( element.parentNode );
        };
        
        this.check = function ( formId, callback )
        {
            checks[formId] = checks[formId] || [];
            checks[formId].push( callback );
        };
        
        var changes = {};
        
        this.change = function ( formId, elementId, value )
        {
            if ( ( formId in changes ) && ( elementId in changes[formId] ) )
            {
                mayaFish.forEach(
                    changes[formId][elementId],
                    function ( key, onChange ) {
                        onChange( value );
                    }
                );
            }
        };
        
        this.multiple = function ( element )
        {
            var values = [], i, l;
            
            if ( element.multiple && element.options.length > 1 )
            {
                for ( i = 0, l = element.options.length; i < l; ++i )
                    if ( element.options[i].selected )
                        values.push( element.options[i].value );
            }
            else // if ( element.className == "dbForm-select-multiple" )
            {
                element = mayaFish.$select( ":checked", element );
                for ( i = 0, l = element.length; i < l; ++i )
                    if ( element[i].checked )
                        values.push( element[i].value );
            }
            
            return values;
        };
        
        this.onChange = function ( formId, elementId, callback )
        {
            changes[formId] = changes[formId] || {};
            changes[formId][elementId] = changes[formId][elementId] || [];
            changes[formId][elementId].push( callback );
            
            var values = [], i, l,
                element = mayaFish.$id( "dbForm-" + formId + "-" + elementId );
            
            if ( element.multiple && element.options.length > 1 )
            {
                for ( i = 0, l = element.options.length; i < l; ++i )
                    if ( element.options[i].selected )
                        values.push( element.options[i].value );
                callback( values );
            }
            else if ( element.value )
                callback( element.value );
            else if ( element.className == "dbForm-select-multiple" )
            {
                element = mayaFish.$select( ":checked", element );
                for ( i = 0, l = element.length; i < l; ++i )
                    if ( element[i].checked )
                        values.push( element[i].value );
                callback( values );
            }
            else if ( element.className == "dbForm-select-one" )
            {
                element = mayaFish.$select( ":checked", element )[0];
                callback( element && element.checked ? element.value : "" );
            }
            else
            {
                callback( "" );
            }
        };
        
        var closeFuncs = [];
        
        this.closeWindows = function ( id )
        {
            mayaFish.forEach( closeFuncs, function ( idx, closeFunc ) {
                closeFunc( id );
            } );
            closeFuncs = [];
        };
        
        this.itemEditorWindow = function ( item, node, create )
        {
            var close = mayaFish.ui.preview(
                '<iframe id="dbFormEditorWindow" allowtransparentcy="true" ' +
                    'src="/dbForm/' + ( create ? "create" : "edit" ) + "/" + item +
                    ( create ? "?formId=" + create.formId +
                        "&amp;parentId=" + create.parentId +
                        "&amp;afterId=" + create.afterId +
                        "&amp;beforeId=" + create.beforeId : "" ) +
                    '"></iframe>',
                true, "#000000"
            );
            
            closeFuncs.push( function ( id ) {
                if ( create && ! id )
                    node.parentNode.removeChild( node );
                else if ( ( id && create && item == "fieldset" ) || ( id &&
                        ! create && node.tagName.toLowerCase() == "fieldset" ) )
                    location.reload();
                else if ( id )
                {
                    mayaFish.httpRequest( {
                        "url": "/dbForm/element/" + id,
                        "parser": "text",
                        "async": true,
                        "onSuccess": function ( result )
                        {
                            var div = document.createElement( "div" );
                            div.innerHTML = result.replace( /\s+/, "" );
                            node.parentNode.replaceChild( div.firstChild, node );
                        },
                        "onFinish": mayaFish.ui.progress()
                    } );
                }
                close();
            } );
        };
        
        this.itemDeleteWindow = function ( item, node )
        {
            mayaFish.ui.confirm(
                mayaFish.translations( "common", "Are you sure" ),
                mayaFish.translations( "common", "Delete" ),
                function ()
                {
                    setTimeout( function () {
                        mayaFish.jsonRpc.call( {
                            "method": "dbFormControl::elementDelete",
                            "params": { "elementId": item },
                            "onSuccess": function ( result )
                            {
                                if ( result.success )
                                {
                                    node.parentNode.removeChild( node );
                                    if ( node.tagName.toLowerCase() == "fieldset" )
                                        location.reload();
                                }
                            },
                            "onFinish": mayaFish.ui.progress()
                        } );
                    }, 100 );
                },
                null,
                mayaFish.ui.buttons.YES | mayaFish.ui.buttons.NO
            );
        };
        
        this.editMode = function ( formId )
        {
            dojo.require( "dojo.dnd.Source" );
            
            var t = this,
                addElements = mayaFish.$id( "dbForm-" + formId + "-add-elements" ),
                outer = mayaFish.$select( "#dbForm-" + formId +
                    " .dbForm-outer-element-container" )[0],
                addDnd = new dojo.dnd.Source( addElements.id, {
                    "accept": [],
                    "copyOnly": true,
                    "selfAccept": false
                } ),
                containerDnds = [],
                containerCreator = function ( item, hint )
                {
                    var node;
                    
                    if ( parseInt( item ) == item )
                    {
                        node = mayaFish.$select( ".dbForm-" + formId +
                            "-element-" + item )[0];
                        
                        if ( hint == "avatar" )
                            node = mayaFish.$select( "label", node )[0].
                                cloneNode( true )
                        else
                        {
                            var div = document.createElement( "div" );
                            div.setAttribute( "dndType", "dbFormElementMove" );
                            div.setAttribute( "dndData", item );
                            div.appendChild( node.cloneNode( true ) );
                            node = div;
                        }
                        
                        return {
                            "node": node,
                            "data": item,
                            "type": ["dbFormElementMove"]
                        };
                    }
                    else
                    {
                        node = document.createElement( "div" );
                        node.id = "mayaFish-dbForm-justInserted";
                        node.innerHTML = "Loading...";
                        
                        return {
                            "node": node,
                            "data": 0,
                            "type": ["dbFormElementMove"]
                        };
                    }
                },
                setupEditMode = function ()
                {
                    containerDnds = [];
                    containers = mayaFish.$select( "#dbForm-" + formId +
                        " .dbForm-element-container" );
                    
                    mayaFish.forEach( containers, function ( key, container )
                    {
                        if ( ! container.id ) container.id = mayaFish.generateId();
                        containerDnds[key] = new dojo.dnd.Source( container.id, {
                            "accept": [ "dbFormElementAdd", "dbFormElementMove" ],
                            "creator": containerCreator,
                            "withHandles": true,
                            "autoSync": true,
                            "onDrop": function ( _source, nodes, copy )
                            {
                                var node = nodes[0],
                                    dndType = node.getAttribute( "dndType" ),
                                    result = dojo.dnd.Source.prototype.onDrop.
                                        call( this, _source, [node], copy ),
                                    target = this.anchor,
                                    parent = target.parentNode.parentNode,
                                    parentId = parent.tagName.toLowerCase() == "fieldset" ?
                                        parent.getAttribute( "data-dbform-element-id" ) : "",
                                    previous = mayaFish.previousElement( target ),
                                    next = mayaFish.nextElement( target ),
                                    previousId, nextId;
                                
                                if ( ! previous )
                                {
                                    previous = mayaFish.previousElement( target.parentNode );
                                    if ( previous && previous.tagName.toLowerCase() == "fieldset" )
                                        previousId = previous.getAttribute( "data-dbform-element-id" );
                                    else
                                        previousId = "";
                                }
                                else
                                    previousId = previous.getAttribute( "dndData" );
                                
                                if ( ! next )
                                {
                                    next = mayaFish.nextElement( target.parentNode );
                                    if ( next && next.tagName.toLowerCase() == "fieldset" )
                                        nextId = next.getAttribute( "data-dbform-element-id" );
                                    else
                                        nextId = "";
                                }
                                else
                                    nextId = next.getAttribute( "dndData" );
                                
                                if ( dndType == "dbFormElementAdd" )
                                {
                                    t.itemEditorWindow(
                                        node.getAttribute( "dndData" ),
                                        target, {
                                        "formId": formId,
                                        "parentId": parentId,
                                        "afterId": previousId,
                                        "beforeId": nextId
                                    } );
                                }
                                else if ( dndType == "dbFormElementMove" )
                                {
                                    setTimeout( function () {
                                        mayaFish.jsonRpc.call( {
                                            "method": "dbFormControl::elementRelocate",
                                            "params": {
                                                "elementId": node.getAttribute( "dndData" ),
                                                "parentId": parentId,
                                                "afterId": previousId,
                                                "beforeId": nextId
                                            },
                                            "onSuccess": function ( result )
                                            {
                                                mayaFish.debug( "DbFormElement move: " +
                                                    ( result.success ? "success" : "fail" ) );
                                            },
                                            "onFinish": mayaFish.ui.progress()
                                        } );
                                    }, 100 );
                                }
                                else
                                    throw new Error( "Unknown dnd type: " + dndType );
                                
                                return result;
                            }
                        } );
                    } );
                };
            
            setupEditMode();
        };
    } );

