/*
   sso-form.js

   Code to assist validation of the SSO Forms

   Depends on:
     YUI Yahoo-Dom-Event
     YUI Element
     form.js

   Expects dump of field data in sso.fields
     - array of objects with properties:
     - n - name (element ID)
     - l - label
     - c - client validation classes as string
     - v - value
     - r - 1 or 0, required status
     - h - 1 or 0, hidden status

   sso.forms is a hash mapping SSO form names to lists of controls that should be validated.
   sso.error will contain page-level error, if any.
   sso.aliens is a hash of alien form control ID-value pairs to be repopulated

*/

// Namespace
var sso;
if (!sso) { sso = new Object(); }

var yui = YAHOO.util; // alias

sso.initialize = function() {
    sso.setupBirthdate();
    sso.setupZipCode();
    sso.setupState();
    sso.setupUsernameValidator();
    try {
        sso.modifyForm();
    } catch (e) {
        form.debug(11, 'exception in modifyform:' + e);
    }
    // If this is a page reload, set the birthdate from the sticky value
    sso.forceBirthdateStickiness();
    sso.forceCountryCodeStickiness();
}
form.safeInit(sso.initialize);


//================================================================
//================================================================
//               Dynamic Form Modification
//================================================================
//================================================================

sso.modifyForm = function() {

    for (var fn in sso.fields) {
        var f = sso.fields[fn];

        // Make sure the HTML actually has this control
        var ctl = yui.Dom.get(f.n);
        if (!ctl) { continue; }

        // Make the control hidden if need be
        ctl = sso.modifyHidden(f, ctl);

        // Populate the value, if known
        sso.modifyValue(f, ctl);

        // Make the control required or optional
        sso.modifyRequired(f, ctl);

        // Setup validation
        sso.modifyValidation(f, ctl);

        // Hide the error message by default
        yui.Dom.setStyle(f.n + '_error', 'visibility', 'hidden');
    }

    sso.modifyAliens();

    // Setup button monitoring
    for (var fm in sso.forms) {
        yui.Event.addListener(fm + '_submit', 'click', sso.setFormSubmitted);
        form.monitorButton( fm + '_submit', sso.forms[fm], sso.immediateValidation);

        // Setup cancel button
        var cnl = yui.Dom.get(fm + '_submit_cancel');
        if (cnl) {
            yui.Event.addListener(cnl, 'click', sso.setFormSubmitted);
            yui.Event.addListener(cnl, 'click', sso.handleCancel);
        }
    }

    // Setup page error
    sso.modifyError();
}


sso.modifyValue = function(f, ctl) {
    if (f.v != null) {
        if (ctl.type == 'checkbox') {
            ctl.checked = f.v;
        } else {
            ctl.value = f.v;
        }
    }
}


sso.modifyRequired = function(f, ctl) {
    if (f.r) {
        //if (f.n == 'birthdate') { form.debug(4, 'birthdate is required '); }
        //if (f.n == 'visualcode') { form.debug(5, 'visualcode is required'); }
        yui.Dom.addClass(ctl, 'formValReq');
        yui.Dom.setStyle(f.n + '_req', 'visibility', 'visible');
    } else {
        yui.Dom.removeClass(ctl, 'formValReq');
        yui.Dom.setStyle(f.n + '_req', 'visibility', 'hidden');
    }
}


sso.modifyValidation = function(f, ctl) {
    //if (f.n == 'birthdate') { form.debug(2, 'birthdate has valclasses ' + f.c); }
    //if (f.n == 'visualcode') { form.debug(3, 'visualcode has valclasses ' + f.c); }
    var classes = f.c.split(' ');
    for (var i = 0; i < classes.length; i++) {
        yui.Dom.addClass(ctl, classes[i]);
    }

    form.attachValidation(f.n);
}


sso.modifyHidden = function(f, ctl) {
    if (!f.h) { return ctl; }

    var grp = yui.Dom.get(f.n + '_group');

    // Remove all children from the group and add a hidden field
    grp.innerHTML = '<input type="hidden" name="' + f.n + '" id="' + f.n + '"/>';

    ctl = yui.Dom.get(f.n);
    return ctl;
}


sso.modifyError = function() {
    if (sso.error) {
        yui.Dom.get('error_message').innerHTML = sso.error;
        yui.Dom.setStyle('error_group', 'display', 'block');
    } else {
        yui.Dom.setStyle('error_group', 'display', 'none');
    }
    var notice_msg = yui.Dom.get('notice_message');
    if (notice_msg) {
        if (sso.notice) {
                notice_msg.innerHTML = sso.notice;
                yui.Dom.setStyle('notice_group', 'display', 'block');
        } else {
            yui.Dom.setStyle('notice_group', 'display', 'none');
        }
    }
}

sso.modifyAliens = function() {
    if (!sso.aliens) { return; }
    for (var an in sso.aliens) {
        var ctl = yui.Dom.get(an);
        if (!ctl) { continue; }
        if (ctl.type == 'checkbox') {
            ctl.checked = sso.aliens[an];
        } else {
            ctl.value = sso.aliens[an];
        }
    }
}

//================================================================
//================================================================
//                   Submit Control
//================================================================
//================================================================

sso.setFormSubmitted = function(evt) {
    var btn = yui.Event.getTarget(evt);
    var parts = btn.id.split('_');
    yui.Dom.get('submitted_form').value = parts[0];
}


// Override
form.skipSubmitButtonValidation = function(buttonID) {
    var parts = buttonID.split('_');
    return yui.Dom.get('submitted_form').value != parts[0];
}

sso.handleCancel = function(evt) {
    var btn = yui.Event.getTarget(evt);

    // Clear all onsubmit handlers from the associated form
    // so we don't go through client-side validation
    yui.Event.removeListener(btn.form, 'submit', undefined); // undef removes all handlers on that event

    return true;
}

//================================================================
//================================================================
//                   Complex Controls
//================================================================
//================================================================


//----------------------------------------------------------------
//                   Birthdate Control
//----------------------------------------------------------------

sso.validate13YOA = function(elem) {
    var val = elem.get('value')
    if (! val) { return true; }

    var bday = (new Date(val)).getTime();
    var today = (new Date()).getTime();
    var thirteen_years_in_msec = 13*365.25*24*60*60*1000;

    return today - bday > thirteen_years_in_msec;

};

// Don't actually install this.  For now, we want 13YOA validation to occur on the server only.
//form.addCustomValidator('formVal13YOA', 'You must be at least 13 years of age to register.', sso.validate13YOA);

sso.setupBirthdate = function() {

    for (var fm in sso.forms) {
        if (! yui.Dom.get(fm + '_birthdate_mm')) { continue; }

        // Setup custom validators for birthdate
        yui.Event.addListener(fm + '_birthdate_mm',   'change', sso.revalidateBirthdateOnEvent);
        yui.Event.addListener(fm + '_birthdate_mm',   'keypress', sso.revalidateBirthdateOnEvent);
        yui.Event.addListener(fm + '_birthdate_dd',   'change', sso.revalidateBirthdateOnEvent);
        yui.Event.addListener(fm + '_birthdate_dd',   'keypress', sso.revalidateBirthdateOnEvent);
        yui.Event.addListener(fm + '_birthdate_yyyy', 'change', sso.revalidateBirthdateOnEvent);
        yui.Event.addListener(fm + '_birthdate_yyyy', 'keypress', sso.revalidateBirthdateOnEvent);
    }

    // Install custom date validator to force 13 or older


    // Override date validator message
    form.errorsByClass['formValDate'] = 'The birthdate does not appear to be a valid date.';
}


sso.revalidateBirthdateOnEvent = function(evt) {
    var btn = yui.Event.getTarget(evt);
    var parts = btn.id.split('_');
    sso.revalidateBirthdate(parts[0]);
}

sso.revalidateBirthdate = function(fm) {
    var bd_mm   = yui.Dom.get(fm + '_birthdate_mm').value;
    var bd_dd   = yui.Dom.get(fm + '_birthdate_dd').value;
    var bd_yyyy = yui.Dom.get(fm + '_birthdate_yyyy').value;

    var bd_check = yui.Dom.get(fm + '_birthdate');
    bd_check.value = bd_mm + '/' + bd_dd + '/' + bd_yyyy;
    form.validateControl(fm + '_birthdate', true);
}


sso.forceBirthdateStickiness = function() {
    for (var fm in sso.forms) {
        var bd_check = yui.Dom.get(fm + '_birthdate');
        if (!bd_check) { continue; }
        if (bd_check.value) {
            var dt = new Date(bd_check.value);
            var num;

            num = dt.getMonth() + 1;
            form.setSelect(fm + '_birthdate_mm', (num < 10 ? '0' + num : num));

            num = dt.getDate();
            form.setSelect(fm + '_birthdate_dd', (num < 10 ? '0' + num : num));

            form.setSelect(fm + '_birthdate_yyyy', dt.getFullYear());
            sso.revalidateBirthdate(fm);
        }
    }
};


//----------------------------------------------------------------
//                   Zip/Postal Code Control
//----------------------------------------------------------------

sso.setupZipCode = function() {

    // Setup custom validators for zip code
    form.addCustomValidator('formValReqSpecialZip','Zip Code is required if you live within the United States.', function() { return true; } );
    form.addCustomValidator('formValReqSpecialPostal','Postal Code is required if you live outside the United States.', function() { return true; });

    for (var fm in sso.forms) {
        if (! yui.Dom.get(fm + '_zip_code')) { continue; }

        form.attachValidation(fm + '_zip_code');
        form.attachValidation(fm + '_postal_code');
        yui.Event.addListener(fm + '_country_code', 'change', sso.updateZipValidationOnChange);
        yui.Event.addListener(fm + '_country_code', 'keypress', sso.updateZipValidationOnChange);
    }
}

sso.updateZipValidationOnChange = function(evt) {
    var btn = yui.Event.getTarget(evt);
    var parts = btn.id.split('_');
    sso.updateZipValidation(parts[0],true);
}

sso.updateZipValidation = function(fm,showError) {
    var zip = yui.Dom.get(fm + '_zip_code');
    if (!zip) { return; }
    var cty = yui.Dom.get(fm + '_country_code').value;

    var show = fm + '_' + (cty == 'us' ? 'zip'    : 'postal') + '_code';
    var hide = fm + '_' + (cty == 'us' ? 'postal' : 'zip') + '_code';

    if (sso.fields[fm + '_zip_code'].r) {
        yui.Dom.removeClass(hide, 'formValReq');
        if (! yui.Dom.hasClass(show, 'formValReq')) { yui.Dom.addClass(show, 'formValReq'); }
    }

    // If country is not US, force clear the zip code
    if (cty != 'us') { zip.value = ''; }
    
    yui.Dom.setStyle(show + '_group','display', 'block');
    yui.Dom.setStyle(hide + '_group','display', 'none');

    form.validateArray([fm + '_zip_code', fm + '_postal_code'], showError);
}


//----------------------------------------------------------------
//                   State/Province Control
//----------------------------------------------------------------

sso.setupState = function() {

    // Setup custom behavior for state/province switching & validation
    form.addCustomValidator('formValReqSpecialProv','Province is required if you live outside the U.S.', function() { return true; } );
    form.addCustomValidator('formValReqSpecialState','State is required if you live within the U.S.', function() { return true; } );

    for (var fm in sso.forms) {
        if (! yui.Dom.get(fm + '_state_code')) { continue; }
        form.attachValidation(fm + '_province');
        form.attachValidation(fm + '_state_code');
        yui.Event.addListener(fm + '_country_code', 'change', sso.updateStateValidationOnChange);
        yui.Event.addListener(fm + '_country_code', 'keypress', sso.updateStateValidationOnChange);
    }
}

sso.updateStateValidationOnChange = function(evt) {
    var btn = yui.Event.getTarget(evt);
    var parts = btn.id.split('_');
    sso.updateStateValidation(parts[0], true);
}

sso.updateStateValidation = function(fm, showError) {
    if (!yui.Dom.get(fm + '_zip_code')) { return; }
    var cty = yui.Dom.get(fm + '_country_code').value;
    var show = fm + '_' + (cty == 'us' ? 'state_code' : 'province'   );
    var hide = fm + '_' + (cty == 'us' ? 'province'   : 'state_code' );

    if (sso.fields[fm + '_zip_code'].r) {
        yui.Dom.removeClass(hide, 'formValReq');
        if (! yui.Dom.hasClass(show, 'formValReq')) { yui.Dom.addClass(show, 'formValReq'); }
    }

    yui.Dom.setStyle(show + '_group','display', 'block');
    yui.Dom.setStyle(hide + '_group','display', 'none');

    form.validateArray([fm + '_state_code', fm + '_province'], showError);
}


//----------------------------------------------------------------
//                   Country Code Control
//----------------------------------------------------------------

sso.forceCountryCodeStickiness = function() {
    for (var fm in sso.forms) {
        if (! yui.Dom.get(fm + '_country_code')) { continue; }
        sso.updateZipValidation(fm, sso.immediateValidation);
        sso.updateStateValidation(fm, sso.immediateValidation);
        form.updateButtonEnable(fm + '_submit', sso.immediateValidation);
    }
}

//----------------------------------------------------------------
//                   Country Code Control
//----------------------------------------------------------------
sso.setupUsernameValidator = function() {
    form.addCustomValidator('notMatchPassword', 'Screen Name must not match your password.', sso.validateUsernameNotMatchPassword);
}

sso.validateUsernameNotMatchPassword = function(elem) {
    // elem  is a yui Element

    // Figure out which for this is so we can get a handle on the Password control
    var form_name = (elem.get('id').split('_'))[0];
    var password = yui.Dom.get(form_name + '_password').value;
    return elem.get('value') != password;
}

//----------------------------------------------------------------
//                   Visual Code Control
//----------------------------------------------------------------
sso.vc = new Object();

sso.vc.IMAGE_COUNT = 16;

sso.vc.imageNumbers = new Object;

sso.vc.initialize = function() {
    // Make list of two-digit image numbers
    for (var i = 1; i <= sso.vc.IMAGE_COUNT; i++) {
        var im = i.toString();
        im = i < 10 ? '0' + im : im;
        sso.vc.imageNumbers[im] = 1;
    }

    sso.vci = new Object;

    for (var fm in sso.forms) {
        var el = yui.Dom.get(fm + '_visualcode');
        if (!el) { continue; }
        sso.vci[fm] = new Object;
        sso.vci[fm].selections = ['00', '00'];
        sso.vci[fm].ctlElem = el;
        sso.vci[fm].clicksUntilWarnings = 2;

        // Install event handlers
        for (var i in sso.vc.imageNumbers) {
            // Bind to image, in case browser down't support clicks for divs
            yui.Event.addListener(fm + '_vc_img_' + i, 'mouseout', sso.vc.handleMouseOut);
            yui.Event.addListener(fm + '_vc_img_' + i, 'mouseover', sso.vc.handleMouseOver);
            yui.Event.addListener(fm + '_vc_img_' + i, 'click', sso.vc.handleClick);
        }
    }

    // Register custom validator
    form.addCustomValidator('formValVC', 'Please select 2 images.', sso.vc.validateSelection);

}
form.safeInit(sso.vc.initialize);


// Called whenever the visual code control gets validated
// Should return true or false regarding whetehr the control validated
sso.vc.validateSelection = function(hiddenCtlElem) {
    var parts = hiddenCtlElem.get('id').split('_');
    var fm = parts[0];

    // Valid if neither selection is '00'
    if (sso.vci[fm].selections[0] == '00') { return false; }
    if (sso.vci[fm].selections[1] == '00') { return false; }
    return true;
}


// An image has been selected.
sso.vc.selectImage = function(fm, imgNum) {
    if (imgNum == '00') { return; }

    // Visually select the image
    yui.Dom.replaceClass(fm + '_vc_div_' + imgNum, 'sso_vc_unselected', 'sso_vc_selected');
    yui.Dom.removeClass(fm + '_vc_div_' + imgNum, 'sso_vc_unfaded');

    // Perform housekeeping on selection list -
    // push new number on selection list, and drop lowest.
    sso.vci[fm].selections.push(imgNum);
    sso.vci[fm].selections.sort();
    sso.vci[fm].selections.splice(0,1);

    // Store new selections in hidden form control
    sso.vc.updateHiddenField(fm);
}


// An image has been deselected.
sso.vc.deselectImage = function(fm, imgNum) {
    if (imgNum == '00') { return; }

    // Visually deselect the image
    yui.Dom.replaceClass(fm + '_vc_div_' + imgNum, 'sso_vc_selected', 'sso_vc_unselected');
    yui.Dom.removeClass(fm + '_vc_div_' + imgNum, 'sso_vc_unfaded');

    // Look for the deselected imgNum in the selection list and replace it with '00'
    for (var i =0; i<2; i++) {
        if (sso.vci[fm].selections[i] == imgNum) {
            sso.vci[fm].selections[i] = '00';
        }
    }
    sso.vci[fm].selections.sort();

    // Store new selections in hidden form control
    sso.vc.updateHiddenField(fm);
}


sso.vc.isSelected = function(fm, imgNum) {
    if (sso.vci[fm].selections[0] == imgNum) { return true; }
    if (sso.vci[fm].selections[1] == imgNum) { return true; }
    return false;
}


sso.vc.divIdToImgNum = function(id) {
    var re = /vc_(div|img)_(\d\d)/;
    var match = re.exec(id);
    return match[2];
}


sso.vc.updateHiddenField = function(fm) {
    var pair = sso.vc.imgNumToKey(fm, sso.vci[fm].selections[0])
             + ','
             + sso.vc.imgNumToKey(fm, sso.vci[fm].selections[1]);
    sso.vci[fm].ctlElem.value = pair;
}


sso.vc.imgNumToKey = function(fm, num) {
    if (num ==  '00') { return '00'; }
    var url = yui.Dom.get(fm + '_vc_img_' + num).src;
    var re = /image\/(.+)$/;
    var match = re.exec(url);
    return match[1];
}


//--------------------------
// Event Handlers
//--------------------------

sso.vc.handleClick = function(evt) {
    var div = yui.Event.getTarget(evt);
    var parts = div.id.split('_');
    var fm = parts[0];

    var imgNum = sso.vc.divIdToImgNum(div.id);
    if (sso.vc.isSelected(fm, imgNum)) {
        // Clicked on an already-selected image - deselect it
        sso.vc.deselectImage(fm, imgNum);
    } else {
        // If 2 images are already selected, the first selection will not be 00
        if (sso.vci[fm].selections[0] != '00') {
            // Then deselect the lower-numbered one
            sso.vc.deselectImage(fm, sso.vci[fm].selections[0]);
        }
        sso.vc.selectImage(fm, imgNum)
    }

    // Update validation
    sso.vci[fm].clicksUntilWarnings--;
    form.validateControl(fm + '_visualcode', (sso.vci[fm].clicksUntilWarnings <= 0));
}


sso.vc.handleMouseOver = function(evt) {

    var div = yui.Event.getTarget(evt);
    var parts = div.id.split('_');
    var fm = parts[0];

    var imgNum = sso.vc.divIdToImgNum(div.id);
    //sso.vc.log('In handlemouseover for imgNum ' + imgNum + ' div has classes ' + div.className);
    if (!sso.vc.isSelected(fm, imgNum)) {
        var div = new yui.Element(fm + '_vc_div_' + imgNum);
        div.replaceClass('sso_vc_unselected', 'sso_vc_unfaded');
    }
}


sso.vc.handleMouseOut = function(evt) {
    var div = yui.Event.getTarget(evt);
    var parts = div.id.split('_');
    var fm = parts[0];

    var imgNum = sso.vc.divIdToImgNum(div.id);
    //sso.vc.log('In handleMouseOut for imgNum ' + imgNum + ' div has classes ' + div.className);
    if (!sso.vc.isSelected(fm, imgNum)) {
        var div = new yui.Element(fm + '_vc_div_' + imgNum);
        div.replaceClass('sso_vc_unfaded', 'sso_vc_unselected');
    }
}
