////////////////////////////////////////////////////////////////////////
//
// Filename:    formbase.js
// Purpose:     Supply base functionality for HTML FORM elements
// Method:      JavaScript class
//
// Author:      J.van.der.Steen@gobase.org
// Date:        2010-03-07
//
// Note:        Requires prototype.js
//
////////////////////////////////////////////////////////////////////////

var FormBase = Class.create( BCBase,
{

    initialize: function($super, values)
    {
        this.valAssign(values, "type", "form" + (this.type === undefined ? '' : '-' + this.type));

        $super(values);

        /**
         * The entity value property definitions
         */
        this.valAssign(values, "properties", {});

        /**
         * Did the form values validate
         */
        this.valAssign(values, "valid", 1);

        /**
         * One of the form entity values was modified
         */
        this.valAssign(values, "dirty", 0);

        /*
         * System properties
         */
        this.propAdd( values
                    , "op"
                    , { type     : 'hidden'
                      , size     : 64
                      , key      : 'op'
                      , val      : 'mail'
                      , valid    : 1
                      , validator: 'validateNonempty'
                      }
                    ) ;
        this.propAdd( values
                    , "_title"
                    , { type     : 'hidden'
                      , size     : 64
                      , key      : '_title'
                      , val      : "No title set in instantiated form class '" + this.type + "'"
                      , valid    : 1
                      , validator: 'validateNonempty'
                      }
                    ) ;

        /**
         * Validation failed or succeeded
         */
        this.valAssign(values, "msgSucceeded", 'Uw aanvraag is ingediend.');
        this.valAssign(values, "msgFailed"   , 'U moet alle verplichte velden (rood) met legale waarden invullen.');

        /**
         * Server handler for save
         */
        this.valAssign(values, "submit", '/control/operation.php');

    }

    /**
     * Set the value of system property '_title'
     */
,   setTitle: function(title)
    {
        return this.propSetVal('_title', title);
    }

    /**
     * Return value of system property '_title'
     */
,   getTitle: function()
    {
        var property = this.propGet('_title');

        return property.val;
    }

    /*
     * Adjust an upload document
     */
,   uploadSetFilename: function(filename)
    {
        var elem;

        if (this.filename != basename(filename)) {
            this.filename = basename(filename);
            this.newdata  = 1;
            // this.setDirty(config.dirty);
        }

        if ((elem = getE(this.type + '-1-' + this.guid)) !== null) {
            elem.value = '';
        }

        //
        // Update GUI widgets
        //
        // this.updateHeader();

        //
        // Immediate save to ensure the document link will work as expected
        //
        // this.save(config.autosaving);

        //
        // Adjust preview (with a delay to enable the document to be saved first)
        //
        // setTimeout( "toPreview('" + this.guid + "'" + ")"
        //           , 2 * 1000
        //           ) ;
    }

    /**
     * Is form uploading data?
     */
,   uploadActive: function()
    {
        if (this.monitor >= 0) {
            return true;
        }
        return false;
    }

    /**
     * Abort any upload
     */
,   uploadAbort: function()
    {
        if (this.monitor >= 0) {
            clearInterval(this.monitor);
            this.monitor = -1;
            this.uploadSetFilename(this.filename);
        }
    }

    /**
     * Data uploading.
     * For documentation, see upload.js
     */
,   setDocument: function(filename)
    {
        var elem;
        var error;

        try {
            //
            // Transfer the document to the Apache server by
            // submitting the "action='control/upload.phtml'" form.
            //
            if ((elem = getE('form-' + this.guid)) !== null) {
                var iframe = getE('iframe-' + this.guid);
                if (iframe !== null) {
                    //
                    // Document will be replaced by the server response.
                    //
                    iframe.contentWindow.document.write('');

                    //
                    // Clean up previous upload
                    //
                    uploadClean(this.type, this.guid, this.filename);

                    //
                    // Install the upload monitor
                    //
                    filename = basename(filename);
                    filename = myEscape(filename,  "'", "_");
                    this.monitorAge = 0;
                    this.monitor    = setInterval( "uploadExec("
                                                             + "'" + this.type + "'"
                                                             + ","
                                                             + "'" + this.guid + "'"
                                                             + ","
                                                             + "'" + filename  + "'"
                                                             + ")"
                                                 , config.uploadInterval
                                                 ) ;
                    uploadReport(this.type, this.guid, filename, true, "started");

                    //
                    // Start transfer to the server
                    //
                    elem.submit();
                } else {
                    error = "A technical error encountered: missing iframe, unable to upload documents\n"
                          + "Please restart your browser"
                          ;
                    uploadError(this.type, this.guid, filename, error);
                }
            } else {
                error = "A technical error encountered: missing form, unable to upload documents\n"
                      + "Please restart your browser"
                      ;
                uploadError(this.type, this.guid, filename, error);
            }
        }
        catch(err) {
            error = '';
            for (prop in err) {
                error += " " + err[prop] + ";";
            }
            uploadError(this.type, this.guid, filename, error);
        }
    }

    /**
     * Add an entity property to this form
     */
,   propAdd: function(values, name, defval)
    {
        try {
            if (values[name] !== undefined && values[name] !== null) {
                this.properties[name] = values[name];
            } else {
                /*
                 * Default entity properties
                 */
                var defprop = { type     : 'text'
                              , size     : 64
                              , key      : ''
                              , val      : ''
                              , valid    : 1
                              , validator: 'validateNonempty'
                              } ;
                this.properties[name] = defval !== null ? defval : defprop;
            }

            //
            // Create symbolic and unique lookup key
            //
            this.properties[name].name = this.type + '-' + name;
            this.properties[name].id   = this.type + '-' + name + '-' + this.guid;

            //
            // Create self-name
            //
            this.properties[name].self = name;

            //
            // Initialize select type with first option value
            //
            if (this.properties[name].type == 'select') {
                this.properties[name].val = this.properties[name].options[0].val;
            }
        }
        catch(err) {
            var error = '';
            for (prop in err) {
                if (err[prop] !== undefined) {
                    error += " " + err[prop] + ";";
                }
            }
            debug("Error: propAdd(): [" + error + "]");
        }
    }

    /**
     * Get an entity property value
     */
,   propGet: function(name)
    {
        return this.properties[name];
    }

    /**
     * Get an entity property value
     */
,   propGetVal: function(name)
    {
        var property = this.propGet(name);

        return property.val;
    }

    /**
     * Set an entity property value
     */
,   propSetVal: function(name, val)
    {
        var property = this.propGet(name);

        property.val = trim(val);
        this.setDirty(1);

        return property.val;
    }

    /**
     * Set dirty bit of this form
     */
,   setDirty: function(dirtylevel)
    {
        this.dirty = dirtylevel;
    }

    /**
     * Enable/disable the form
     */
,   setFormEnable: function(enabled)
    {
        if (enabled === 0) {
            var elem = getE('form-' + this.guid);
            if (elem !== null) {
                elem.disable();
            }
        }
    }

    /**
     * Update valid state of property
     */
,   propSetValid: function(property, valid)
    {
        property.valid = valid;

        var elem = getE(property.id);
        if (elem !== null) {
            var klass1 = property.valid === 1 ? 'keyvalid' : 'keyinvalid';
            var klass0 = property.valid === 0 ? 'keyvalid' : 'keyinvalid';
            removeClassName(elem, klass0);
               addClassName(elem, klass1);
        }
    }

    /**
     * Render an input field (text, hidden, password, button, ...) as HTML
     */
,   fieldInputToHTML: function(property)
    {
        var lookup  = "config.lookupTable.get('" + this.guid + "')";
        var name    = property.self;
        var id      = '';
        var onclick = '';
        var html    = '';

        if (property.onclick !== undefined) {
            onclick = " onclick=\"" + lookup + "." + property.onclick + "('" + name + "',this.value);\"";
        }

        if (property.type == 'button') {
            id = " id='" + property.id + "'";
        }

        html += "<input"
              + " type='"  + property.type         + "'"
              +   id
              + " name='"  + property.name         + "'"
              + " value='" + this.propGetVal(name) + "'"
              + " size='"  + property.size         + "'"
              + " onkeyup=\"" + lookup + ".propSetVal('" + name + "',this.value);\""
              +   onclick
              + ">\n"
              ;

        return html;
    }

    /**
     * Render an input field as HTML
     */
,   fieldTextareaToHTML: function(property)
    {
        var lookup  = "config.lookupTable.get('" + this.guid + "')";
        var name    = property.self;
        var html    = '';

        html += "<textarea"
              + " name='"  + property.name         + "'"
          //  + " value='" + this.propGetVal(name) + "'"
              + " rows='"  + property.rows         + "'"
              + " cols='"  + property.cols         + "'"
              + " onkeyup=\"" + lookup + ".propSetVal('" + name + "',this.value);\""
              + ">\n"
              ;
        html += this.propGetVal(name);
        html += "</textarea>\n";

        return html;
    }

    /**
     * Render a picklist field as HTML
     */
,   fieldSelectToHTML: function(property)
    {
        var lookup  = "config.lookupTable.get('" + this.guid + "')";
        var name    = property.self;
        var html    = '';

        html += "<select"
              + " onchange=\"" + lookup + ".propSetVal('" + name + "',this.value);\""
              + ">\n"
              ;
        var options = property.options;
        for (var n = 0; n < options.length; n++) {
            var option = options[n];
            html += "<option"
                  + " name='"  + option.key + "'"
                  + " value='" + option.val + "'"
                  + ">\n"
                  + option.val
                  + "</option>"
                  ;
        }
        html += "</select>\n";

        return html;
    }

    /**
     * Enable the user to edit this form
     */
,   edit: function()
    {
        var html   = '';

        html += "<form id='form-" + this.guid + "'"
              + " method='POST'"
              + " onSubmit='return false;'"
              + ">"
              + "\n"
              ;

        html += "<center>\n";
        html += "<table summary='" + this.type + "'>\n";

        for (var name in this.properties) {
            if (this.properties[name]      !== undefined &&
                this.properties[name].type !== 'hidden'  &&
                this.properties[name].type !== 'button'
               )
            {
                var property = this.properties[name];
                var klass    = property.valid === 1 ? 'keyvalid' : 'keyinvalid';

                html += "<tr>";
                html += "<th"
                      + " class='" + klass       + "'"
                      + " id='"    + property.id + "'"
                      + ">"
                      + property.key
                      + "</th>\n"
                      ;
                html += "<td class='val'>";
                switch (property.type) {
                    case 'text':
                        html += this.fieldInputToHTML(property);
                        break;
                    case 'password':
                        html += this.fieldInputToHTML(property);
                        break;
                    case 'textarea':
                        html += this.fieldTextareaToHTML(property);
                        break;
                    case 'select':
                        html += this.fieldSelectToHTML(property);
                        break;
                }
                html += "</td>\n";
                html += "</tr>\n";
            }
        }

        html += "</table>\n";
        html += "</center>\n";

        //
        // Emit buttons
        //
        html += "<p style='text-align: left; margin: 16px 0px 16px 300px;'>\n";
        for (var name in this.properties) {
            if (this.properties[name] !== undefined && this.properties[name].type === 'button') {
                var property = this.properties[name];

                html += this.fieldInputToHTML(property);
            }
        }
        html += "</p>\n";

        //
        // Emit hidden fields
        //
        for (var name in this.properties) {
            if (this.properties[name] !== undefined && this.properties[name].type === 'hidden') {
                var property = this.properties[name];

                html += this.fieldInputToHTML(property);
            }
        }

        html += "</form>\n";

        //
        // Invalid message
        //
        html += "<p id='form-feedback-" + this.guid + "'"
              + " style='text-align: left; margin: 16px 0px 16px 0px;'"
              + ">\n"
              ;
        html += "</p>\n";

        return html;
    }

,   validateNonempty: function(name)
    {
        var property = this.propGet(name);

        return property.val !== '';
    }

,   validatePhone: function(name)
    {
        var property = this.propGet(name);

        if (property.val === '') {
            //
            // Empty is ok
            //
            return true;
        }

        //
        // [0]123456789
        //
        var reg = /^([0-9-]{9,11})$/;
        var phone = property.val;

        if (reg.test(phone) === false) {
            return false;
        }
        return true;
    }

,   validateEmail: function(name)
    {
        var property = this.propGet(name);

        //
        // a.a.a.a@b.b.b.b.b.b.b.pq[rs]
        //
        var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
        var address = property.val;

        if (reg.test(address) === false) {
            return false;
        }
        return true;
    }

    /**
     * Validate the submitted form values
     */
,   validateForm: function(parameters)
    {
        //
        // Assume all is fine
        //
        this.valid = 1;

        //
        // Gather and evaluate supplied property values
        //
        for (var name in this.properties) {
            if (this.properties[name] !== undefined) {
                var property = this.properties[name];

                if ( property.validator === null ||
                     eval("this." + property.validator + "('" + name + "')") === true
                ) {
                    this.propSetValid(property, 1);
                } else {
                    this.propSetValid(property, 0);
                    this.valid = 0;
                }
                if (property.type == 'hidden') {
                    parameters     [property.self] = property.val;
                } else {
                    parameters.form[property.self] = property.val;
                }
            }
        }

        return this.valid;
    }

    /**
     * Give feedback about form submission
     */
,   setFormFeedback: function(msg)
    {
        Element.update('form-feedback-' + this.guid, msg);
    }

    /**
     * Validate and save all submitted form values
     */
,   save: function(name, val)
    {
        var parameters = { type: this.type
                         , guid: this.guid
                         , form: {}
                         , user: config.user
                         } ;

        debug('');

        this.valid = this.validateForm(parameters);

        parameters.form = Object.toJSON(parameters.form);
        parameters.user = Object.toJSON(parameters.user);

        if (this.valid) {
            //
            // Submit
            //
            var request = new Ajax.Request( this.submit
                                          , { method: 'post'
                                            , onFailure: this.opFailed
                                            , onSuccess: this.opSuccess
                                            , parameters: parameters
                                            }
                                          ) ;
            if (this.msgSucceeded !== '') {
                this.setFormFeedback(this.msgSucceeded);
            }
            this.setFormEnable(0);
        } else {
            if (this.msgFailed !== '') {
                this.setFormFeedback(this.msgFailed);
            }
        }

    }

,   opFailed: function(transport)
    {
        var person = config.board.webmaster;

        debug("Submit failed, please contact: <a href=mailto:'" + person.email + "'>" + person.name + "</a>");
    }

    /**
     * Pick up any PHP warnings/errors from an Ajax response
     */
,   extractErrorsPHP: function(transport)
    {
        var errors = '';

        if (transport.responseText.match(/PHP:/) !== null) {
            lines = transport.responseText.split("\n");
            for (var n = 0; n < lines.length; n++) {
                if (lines[n].match(/PHP:/) === null) {
                    //
                    // No more PHP messages
                    //
                    break;
                }
                errors += lines[n];
            }
        }
        return errors;
    }

,   extractErrorsJSON: function(transport)
    {
        var response = transport.responseText.evalJSON();
        var errors = '';

        if (response.error !== undefined && response.error !== '') {
            errors = response.error;
        }
        return errors;
    }

,   extractErrors: function(transport)
    {
        var errors = '';

        if ((errors = FormBase.prototype.extractErrorsPHP (transport)) !== '') {
            return errors;
        }
        if ((errors = FormBase.prototype.extractErrorsJSON(transport)) !== '') {
            return errors;
        }

        return errors;
    }

    /**
     * Analyze Ajax response and return form object or null in case of an error.
     */
,   opSuccess: function(transport)
    {
        var errors = '';

        if ((errors = FormBase.prototype.extractErrors(transport)) !== '') {
            debug("Errors: '" + errors + "'");
            // showModalAlert(errors);
            // this.saveFailed(transport);

            return null;
        }

        var response = transport.responseText.evalJSON();
        if (response      === null      ||
            response.type === undefined ||
            response.guid === undefined
        ) {
            debug("Errors: '" + "response has illegal structure" + "'");
            return null;
        }

        response.theform = config.lookupTable.get(response.guid);
        if (response.theform === null) {
            debug("Errors: '" + "response has illegal form reference" + "'");
            return null;
        }

        return response;
    }

} ) ;


