﻿$.fn.reverse = [].reverse;

$.easing.elasout = function(x, t, b, c, d) {
	var s=1.70158;var p=0;var a=c;
	if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
	if (a < Math.abs(c)) { a=c; var s=p/4; }
	else var s = p/(2*Math.PI) * Math.asin (c/a);
	return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
};

$.easing.jswing             = jQuery.easing.jswing;
$.easing.def                = jQuery.easing.def;

$.easing.easeInQuad         = jQuery.easing.easeInQuad;
$.easing.easeInCubic        = jQuery.easing.easeInCubic;
$.easing.easeInQuart        = jQuery.easing.easeInQuart;
$.easing.easeInQuint        = jQuery.easing.easeInQuint;
$.easing.easeInSine         = jQuery.easing.easeInSine;
$.easing.easeInExpo         = jQuery.easing.easeInExpo;
$.easing.easeInCirc         = jQuery.easing.easeInCirc;
$.easing.easeInElastic      = jQuery.easing.easeInElastic;
$.easing.easeInBack         = jQuery.easing.easeInBack;
$.easing.easeInBounce       = jQuery.easing.easeInBounce;

$.easing.easeOutQuad        = jQuery.easing.easeOutQuad;
$.easing.easeOutCubic       = jQuery.easing.easeOutCubic;
$.easing.easeOutQuart       = jQuery.easing.easeOutQuart;
$.easing.easeOutQuint       = jQuery.easing.easeOutQuint;
$.easing.easeOutSine        = jQuery.easing.easeOutSine;
$.easing.easeOutExpo        = jQuery.easing.easeOutExpo;
$.easing.easeOutCirc        = jQuery.easing.easeOutCirc;
$.easing.easeOutElastic     = jQuery.easing.easeOutElastic;
$.easing.easeOutBack        = jQuery.easing.easeOutBack;
$.easing.easeOutBounce      = jQuery.easing.easeOutBounce;

$.easing.easeInOutQuad      = jQuery.easing.easeInOutQuad;
$.easing.easeInOutCubic     = jQuery.easing.easeInOutCubic;
$.easing.easeInOutQuart     = jQuery.easing.easeInOutQuart;
$.easing.easeInOutQuint     = jQuery.easing.easeInOutQuint;
$.easing.easeInOutSine      = jQuery.easing.easeInOutSine;
$.easing.easeInOutExpo      = jQuery.easing.easeInOutExpo;
$.easing.easeInOutCirc      = jQuery.easing.easeInOutCirc;
$.easing.easeInOutElastic   = jQuery.easing.easeInOutElastic;
$.easing.easeInOutBack      = jQuery.easing.easeInOutBack;
$.easing.easeInOutBounce    = jQuery.easing.easeInOutBounce;

var socialCircle = {
// -- fade helper from http://www.codylindley.com/blogstuff/js/jquery/# ---------------------------
// [tweaked slightly]
//
    fadeHelper: {
        easeInOut: function (minValue, maxValue, totalSteps, actualStep, powr) {
	        var delta = maxValue - minValue;
	        var stepp = minValue + (Math.pow (((1 / totalSteps) * actualStep), powr) * delta);
        	
	        return Math.ceil (stepp);
        },

        addFade: function () {
            addFadeToElem (this);
        },

    	// socialCircle.fadeHelper.addFadeToElem (elem, config)
    	//
        addFadeToElem: function (elem, config) {
            if (elem.jquery) {
                elem = elem.get (0);
            }
            if (!config) {
                config = {
                };
            }
            if (!config.fromCol) {
                config.fromCol = [0xff, 0xff, 0x40];
            }
            if (!config.toCol) {
		        config.toCol = [0xff, 0xff, 0xff];
            }
            if (!config.finalCol) {
		        config.finalCol = 'transparent';
            }
            if (!config.steps) {
		        config.steps = 30;
            }
            if (!config.intervals) {
		        config.intervals = 30;
            }
            if (!config.powr) {
		        config.powr = 4;
            }
	        socialCircle.fadeHelper.doBGFade (
	            elem, 
	            config.fromCol, 
	            config.toCol, 
	            config.finalCol, 
	            config.steps, 
	            config.intervals, 
	            config.powr
	        );
        },
    	
    	// socialCircle.fadeHelper.doBGFade
        doBGFade: function (elem, startRGB, endRGB, finalColor, steps, intervals, powr) {
	        if (elem.bgFadeInt) {
	            window.clearInterval (elem.bgFadeInt);
            }
            var easeInOut = socialCircle.fadeHelper.easeInOut;
            
	        var actStep = 0;
	        elem.bgFadeInt = window.setInterval (
	            function () {
		            elem.style.backgroundColor = "rgb(" +
			            easeInOut (startRGB [0], endRGB [0], steps, actStep, powr)+ "," +
			            easeInOut (startRGB [1], endRGB [1], steps, actStep, powr)+ "," +
			            easeInOut (startRGB [2], endRGB [2], steps, actStep, powr)+ 
	                ")";
            			
		            actStep++;
		            if (actStep > steps) {
		                elem.style.backgroundColor = finalColor;
		                window.clearInterval (elem.bgFadeInt);
		            }
	            }, intervals
	        );
        }
    },

// -- (end) fade helper from http://www.codylindley.com/blogstuff/js/jquery/# ---------------------
    
    
    
    
    
    
    
    
    scrollHelper: {
        // socialCircle.scrollHelper.addScrollToHashLinks ($container, fnAfterScroll);
        //
        addScrollToHashLinks: function ($container, afterScroll) {
            $container.find('a').click(function(){
                var target  = $(this.hash);
                
                $.scrollTo( target, {
                    duration: 200,
                    offset: { top: -60, left: 0 },
                    easing: "easeOutExpo",
                    onAfter: function () {
                        if (afterScroll) {
                            afterScroll (target);
                        }
                    }
                });
                return false;
            });
        },
        
        // socialCircle.scrollHelper.hilightCol
        //
        hilightCol: [0xff, 0xff, 0xa0],
        
        // socialCircle.scrollHelper.scrollToAndHilight (target, [0xff, 0xff, 0xff]);
        //
        scrollToAndHilight: function (target, toColorArray) {
            toColorArray = toColorArray || [0x63, 0xc9, 0xff];
            
            $.scrollTo( target, {
                duration: 200,
                offset: { top: -60, left: 0 },
                easing: "easeOutExpo",
                onAfter: function () {
                    (function () {
                        socialCircle.fadeHelper.addFadeToElem (target, {
                            fromCol:    socialCircle.scrollHelper.hilightCol,
                            toCol:      toColorArray,
                            finalCol:   toColorArray
                        });
                    }) ();
                }
            });
        },
        
        // socialCircle.scrollHelper.scrollContainerAndHilight (container, target, [0xff, 0xff, 0xff]);
        //
        scrollContainerAndHilight: function (container, target, toColorArray) {
            toColorArray = toColorArray || [0x63, 0xc9, 0xff];
            
            container.scrollTo( target, {
                duration: 200,
                offset: { top: -60, left: 0 },
                easing: "easeOutExpo",
                onAfter: function () {
                    (function () {
                        socialCircle.fadeHelper.addFadeToElem (target, {
                            fromCol:    socialCircle.scrollHelper.hilightCol,
                            toCol:      toColorArray,
                            finalCol:   toColorArray
                        });
                    }) ();
                }
            });
        }
    },
    
    
    
    formHelper: {
        pleaseChoose: "-- please choose --",
        
        // socialCircle.formHelper.augmentCss ();
        //
        augmentCss: function () {
            // apply a class to checkbox and radio-button labels
            //
            $('label').each (function (index) {
                // only labels which are associated with checkboxes
                //
                var field   = $(this);
                
                var targetId = field.attr ('for');
                if (targetId) {
                    $('#' + targetId + ':checkbox').each (function () {
                        field.addClass ('checkboxLabel')                
                    });
                    $('#' + targetId + ':radio').each (function () {
                        field.addClass ('radioLabel')                
                    });
                }
            });
        },
        
        // socialCircle.formHelper.prepareLeeForm ()
        //
        prepareLeeForm: function () {
            $('.leeForm input[type=text], .leeForm input[type=password], .leeForm textarea').addClass ('textField');
            $('.leeForm input[type=text], .leeForm input[type=password], .leeForm textarea, .leeForm select').not ('.optionalField').addClass ('requiredField');
            $('.leeForm .optGroup').not ('.optionalField').addClass ('requiredField');
            $('.leeForm .submit[disabled]').addClass ('disabled');
            
            $('.leeForm input[type=radio], .leeForm input[type=checkbox]').addClass('checkableInput').addClass ('styled');
            $('.leeForm label').not ('.leeForm .optGroup label').filter (function (index) {
                // don't append a colon to labels associated with checkboxes
                //
                var field = $(this);
                var ok = true;
                
//              if (field.attr ('tagName').toLowerCase () === "label") {
                    var targetId = field.attr ('for');
                    if (targetId) {
                        $('#' + targetId + ":checkbox").each (function () {
                            ok = false;
                        });
                    }
//              }
                
                return ok;
            }).append (':');
            
            $('.requiredField').tooltip (socialCircle.domHelper.applyTooltipDefaults ({ 
                bodyHandler: function() { 
                    var field = $(this);
                    
                    var thumbHtml = "";
                    
                    var errors = field.get (0).validationErrors;
                    
                    function addRow (msg) {
                        thumbHtml.push ("<tr><td colspan='2'><span class='label'>");
                        thumbHtml.push (msg);
                        thumbHtml.push ("</span></td>");
                    }

                    if (errors && errors.length > 0) {
                        thumbHtml   = [
                            "<table>"
                        ];
                        
                        for (var i = 0, len = errors.length; i < len; i++) {
                            addRow (errors [i].message);
                        }
                            
                        thumbHtml.push ("</table>");
                        
                        thumbHtml = thumbHtml.join ('');
                    }
                    
                    return thumbHtml; 
                }
            }));

            $('.leeForm input, .leeForm .optGroup *, .leeForm select, .leeForm textarea').not ('.leeForm input[type=submit]').focus(function (e) {
                var field = $(this);

                field.addClass ('selectedField');
                
                var optGroup = field.parents ('.optGroup');
                
                if (optGroup.length >= 1) {
                    $(optGroup [0]).addClass ('selectedField');
                }
            }).blur (function (e) {
                var field = $(this);
                field.removeClass ('selectedField');

                var optGroup = field.parents ('.optGroup');
                if (optGroup.length >= 1) {
                    $(optGroup [0]).removeClass ('selectedField');
                }
            });
            
            $('.leeForm .optGroupSelAll').click (function (e) {
                var optGroup = $(this).parents ('.optGroup');
                
                socialCircle.formHelper.setDescendantCheckboxesTo (optGroup, true);
                e.preventDefault ();
            });
            
            $('.leeForm .optGroupSelNone').click (function (e) {
                var optGroup = $(this).parents ('.optGroup');
                
                socialCircle.formHelper.setDescendantCheckboxesTo (optGroup, false);
                e.preventDefault ();
            });
            
            socialCircle.formHelper.updateLabelsToShowRequiredFields ();
        },
        
        // socialCircle.formHelper.updateLabelsToShowRequiredFields ()
        //
        updateLabelsToShowRequiredFields: function () {
            // remove the 'synthetic' elements generated to work around IE6's lack of :after
            //
            $('.requiredFieldLabel_after').remove ();
            
            $('.leeForm .requiredField').each (function () {
                var field = $(this);
                
                // find the label in the first cell in the row
                //
                field.parents ('tr').find ('td:first label').addClass ('requiredFieldLabel');
            });
            $('.leeForm .optionalField').each (function () {
                var field = $(this);
                
                // find the label in the first cell in the row
                //
                field.parents ('tr').find ('td:first label').removeClass ('requiredFieldLabel');
            });
            
            // add 'synthetic' elements, generated to work around IE6's lack of :after
            //
            $('.requiredFieldLabel').each (function () {
                $(this).after (
                    $(document.createElement ('span'))
                        .addClass ('requiredFieldLabel_after')
                        .html ("*")
                );
            });
        },
        
        // socialCircle.formHelper.trim (jqElem)
        //
        trim: function (jqElem) {
            var mayTrim = false;
            
            switch (jqElem.attr ('tagName').toLowerCase ()) {
            case 'input':
                switch (jqElem.attr ('type').toLowerCase ()) {
                case 'password':
                case 'text':
                    mayTrim = true;
                    break;
                }
                break;
            case 'textarea':
                mayTrim = true;
                break;
                
            default:
                if (jqElem.hasClass ('optGroup')) {
                    jqElem.find ('input[type=radio], input[type=checkbox]').each (function () {
                        // recursively trim fields within a group
                        //
                        socialCircle.formHelper.trim ($(this));
                    });
                }
                break;
            }
            if (mayTrim) {
                var trimVal = jqElem.val ();
                if (trimVal) {
                    trimVal = jQuery.trim (trimVal);
                }
                jqElem.val (trimVal);
            }
        },

        // socialCircle.formHelper.hasValue (jqElem)
        //
        hasValue: function (jqElem) {
            var valueFound = false;
            
            switch (jqElem.attr ('tagName').toLowerCase ()) {
            case 'input':
                switch (jqElem.attr ('type').toLowerCase ()) {
                case 'password':
                case 'text':
                    valueFound = jqElem.val () != "";
                    break;
                case 'checkbox':
                    valueFound = jqElem.attr ('checked') != "";
                    break;
                case 'radio':
                    valueFound = jqElem.attr ('checked') != "";
                    break;
                }
                break;
            case 'textarea':
                valueFound = jqElem.val () != "";
                break;
                
            case 'select':
                valueFound = jqElem.val () != socialCircle.formHelper.pleaseChoose;
                break;
                
            default:
                if (jqElem.hasClass ('optGroup')) {
                    jqElem.find ('input[type=radio], input[type=checkbox]').each (function () {
                        // a single radio or checkbox checked within the group 
                        // may be interpreted as the group having value for validation purposes
                        //
                        if (socialCircle.formHelper.hasValue ($(this))) {
                            valueFound = true;
                            return false;
                        }
                    });
                }
                break;
            }
            
            return valueFound;
        },
        
        // socialCircle.formHelper.validateFormFields (jqForm, config)
        //
        //
        // config is an optional object of the form:
        //  {
        //      // optional - maps an id to a validationFunc by id
        //      //
        //      validationHandlers: {
        //          formId1:    function (formField) { 
        //              return true if valid; or an object of the form:
        //              {
        //                  valid:      true or false,
        //                  message:    validation message
        //              }
        //          }, // validationFunc1
        //          formId2:    function (formField) { return true if valid... as previously; } // validationFunc2
        //      },
        //
        //      // optional - maps a form field to a validationFunc
        //      //
        //      getValidationHandler: function (formField) {
        //
        //          // determine which validation func to return, each of the form:
        //          //      function (formField) { 
        //                      return true if valid; or an object of the form:
        //                      {
        //                          valid:      true or false,
        //                          message:    validation message
        //                      }
        //          //      }
        //          //
        //          return somehowMapFormFieldToValidationFunc (formField);
        //      },
        // ----------------------------------------------------------------------------------------
        //      // optional - maps an id to a function which applies/removes a visual indicator
        //      // meaning 'failed validation'
        //      // e.g. if the field holding the data has been replaced by a rich editor or flash component.
        //      //
        //      validationFailureMarkerHandlers: {
        //          formId1:    function (formField, valid) { // show/hide marker dependent upon 'valid' flag },
        //          formId2:    function (formField, valid) { // show/hide marker dependent upon 'valid' flag }
        //      },
        //
        //      // optional - maps a form field to a validationFailureHandler
        //      //
        //      getValidationFailureMarkerHandler: function (formField, valid) {
        //          // show/hide marker dependent upon 'valid' flag
        //      }
        // ----------------------------------------------------------------------------------------
        //      // optional - maps an id to an action to take which indicates the validation-failure to the user
        //      //
        //      validationFailureHandlers: {
        //          formId1:    function (formField) { // do something to indicate validation failure },
        //          formId2:    function (formField) { // do something to indicate validation failure }
        //      },
        //
        //      // optional - maps a form field to a validationFailureHandler
        //      //
        //      getValidationFailureHandler: function (formField) {
        //          // do something to indicate validation failure for the formField
        //      }
        //  }
        //
        //  Note: both validationHandlers and getValidationHandler may be present - 
        //      if both specify validationFunctions for a particular field, the first to return a value validates the field.
        //
        //  same comment for validationFailureHandlers and getValidationFailureHandler - 
        //      the validationFailureHandlers-entry will take precendence
        //
        validateFormFields: function (jqForm, config) {
            var valid               = true;
            var form                = jqForm;
            var newlyFailedFields   = [];
            
            form.find ('.requiredField').each (function () {
                var field = $(this);
                
                field.get (0).validationErrors = [];
                
                var validationFuncs = [];
                
                if (
                    config && 
                    config.validationHandlers && 
                    field.attr ('id') &&
                    config.validationHandlers [field.attr ('id')]
                ) {
                    validationFuncs.push (config.validationHandlers [field.attr ('id')]);
                }


                if (
                    config && 
                    config.getValidationHandler (field)
                ) {
                    validationFuncs.push (config.getValidationHandler (field));
                }

                validationFuncs.push (function (jqElem) {
                    var hasValue = socialCircle.formHelper.hasValue (jqElem);
                        
                    var result = {
                        valid:      hasValue,
                        message:    hasValue ? "Valid" : "Value required"
                    };
                        
                    return result;
                });
                
                // trim the field's value
                //
                socialCircle.formHelper.trim (field);
                
                // test validity
                //
                var result = false, validationResult;
                for (var i = 0; i < validationFuncs.length; i++) {
                    validationFunc = validationFuncs [i];

                    validationResult = validationFunc (field);

                    if (validationResult !== undefined && validationResult !== null) {
                        // break as soon as the field has been validated
                        result = validationResult;
                        break;
                    }
                }
                
                if (typeof (result) !== 'object') {
                    result = {
                        valid: result
                    };
                }
                if (!result.message) {
                    message = (result.valid ? "Valid" : "Value required")
                }
                
                
                // decide how absence of validity is to be demonstrated to the user
                //
                var markerFunc;
                if (
                    config && 
                    config.validationFailureMarkerHandlers && 
                    field.attr ('id') && 
                    (markerFunc = config.validationFailureMarkerHandlers [field.attr ('id')])
                ) {}
                else if (
                    config && 
                    config.getValidationFailureMarkerHandler && 
                    (markerFunc = config.getValidationFailureMarkerHandler (field))
                ) {}
                else {
                    markerFunc = function (formField, result) {
                        if (result.valid) {
                            formField.removeClass ('requiredFieldNeedsAttention');
                        }
                        else {
                            formField.addClass ('requiredFieldNeedsAttention');
                            formField.get (0).validationErrors.push (result);
                        }
                    };
                }

                // show a visual indicator to indicate validation failure/success
                //
                if (result.valid) {
                    markerFunc (field, result);
                }
                else {
                    newlyFailedFields.push (field)
                    valid = false;
                    markerFunc (field, result);
//                  return false;;
                }
            });
            
            if (valid) {
            }
            else if (!valid) {
                // scroll the page to the first field which needs attention.
                // assumes that the relative vertical order of the form fields 
                // is the same as the definitions of those fields within the document
                //
                if (newlyFailedFields.length >= 1) {
                    var field = $(newlyFailedFields [0]);
                    
                    var validationFailureHandler;
                    
                    if (
                        config && 
                        config.validationFailureHandlers && 
                        field.attr ('id') && 
                        (validationFailureHandler = config.validationFailureHandlers [field.attr ('id')])
                    ) {}
                    else if (
                        config &&
                        config.getValidationFailureHandler &&
                        (validationFailureHandler = config.getValidationFailureHandler (field))
                    ) {}
                    else {
                        validationFailureHandler = function (formField) {
                            window.setTimeout (function () {
                                $.scrollTo (formField, {
                                    duration: 200,
                                    offset: { top: -60, left: 0 },
                                    onAfter: function () {
                                        window.setTimeout (function () { field.focus (); }, 0);
                                    },
                                    easing: "easeOutExpo"
                                });
                            }, 0);
                        }
                    }
                    
                    validationFailureHandler (field);
                    
                    return false;
                }
            }
            
            return valid;
        },
        
        stringValidation: {
            // socialCircle.formHelper.stringValidation.isIntegerWithinRange (val, lower, upper);
            //
            // the string val holds an integer within the specified range (doubly-inclusive)
            //
            isIntegerWithinRange: function (val, lower, upper) {
                var valid = false;
                
                if (socialCircle.formHelper.stringValidation.isInteger (val)) {
                    val = parseInt (val);
                    
                    valid = (val >= lower) && (val <= upper);
                }
                
                return valid;
            },
            
            // socialCircle.formHelper.stringValidation._re_isInteger;
            //
            // regex which ensures that the input string consists solely of a decimal integer
            //
            _re_isInteger: /^[0-9]+$/,
            
            
            // socialCircle.formHelper.stringValidation.isInteger (val);
            //
            // the string val holds an integer
            //
            isInteger: function (val) {
                var valid = !!val.match (socialCircle.formHelper.stringValidation._re_isInteger);
              
                return valid;  
            }
        },

        // socialCircle.formHelper.setChildCheckboxesTo (container, checked)
        //
        // container is a jquery object
        // checked is boolean (true or false)
        //
        setDescendantCheckboxesTo: function (container, checked) {
            var checkboxes = container.find ('input[type=checkbox]').each (function () {
                if (checked) {
                    $(this).attr('checked', 'checked');
                }
                else {
                    $(this).removeAttr ('checked');
                }
            });
        },
        
        // socialCircle.formHelper.focusWhenReady (id);
        //
        focusWhenReady: function (id) {
            $(document).ready (function () {
                $('#' + id).focus ();
            });
        },
        
        // socialCircle.formHelper.applyWatermark (config);
        //
        // config is an object of the form:
        //  {
        //      fieldId:        "fieldId",
        //      text:           "watermark text",
        //      watermarkClass: "cssClasForWatermark"
        //  }
        //
		applyWatermark: function (config) {
		    if (!config.watermarkClass) { config.watermarkClass = "watermark" }
		    
		    function setWatermark (field) {
	            if (field.val () == "") {
	                field.addClass (config.watermarkClass);
	                field.val (config.text);
	            }
	            
//	            $(window).unload (function () {
//	                alert ("unloading: " + field.attr ('name'));
//	                unsetWaterMark (field);
//	            });
		    }
		    
		    function unsetWatermark (field) {
	            if (field.val () == config.text) {
	                field.val ("");
	                field.removeClass (config.watermarkClass);
	            }
		    }
		    
		    setWatermark ($('#' + config.fieldId));
		    
	        $('#' + config.fieldId).focus (function () {
	            var field = $(this);
	            
	            unsetWatermark (field);
	        }).blur (function () {
	            var field = $(this);
    	        
    	        setWatermark (field);
	        });
	    },
	    
        // socialCircle.formHelper._eventEditorsMarker;
        //
        _eventEditorsMarker: '<!-- event description -->',
        
        // socialCircle.formHelper.doEventEditorsSubmit (isEvent);
        //
        // validates the fields on the two pages which allow creation/editing of events - these should be centralised
        //
	    doEventEditorsSubmit: function (isEvent, isNewTemplate, isTemplateClone) {

            var whatsOnHeaderValid  = false;
            var whatsOnTextValid    = false;
            if (isEvent === undefined) var isEvent = true;
            if (isNewTemplate === undefined) var isNewTemplate = false;
            
            function combinedWhatsOnValid () {
                return whatsOnHeaderValid && whatsOnTextValid; 
            }

            // prepareForSubmit
            //
            var eventDescContent = eventDescEditor.getData ();
            
            if (eventDescContent && eventDescContent.indexOf (socialCircle.formHelper._eventEditorsMarker) == -1) {
                eventDescEditor.setData (socialCircle.formHelper._eventEditorsMarker + eventDescContent);
            }
            else if (!eventDescContent) {
                eventDescEditor.setData (socialCircle.formHelper._eventEditorsMarker);
            }
        
            var whatsOnFieldValues = getWhatsOnFieldValues ();
            
            $('#whatsOnHeaderField').val (whatsOnFieldValues.whatsOnHeader);
            $('#whatsOnTextField').val ( whatsOnFieldValues.whatsOnText);
            $('#whatsOnFooterField').val (whatsOnFieldValues.whatsOnFooter);

            return socialCircle.formHelper.validateFormFields (
                $('#eventForm'), {
                    validationHandlers: {
                        'eventType': function (formField) {
                            // always valid - it's an optional field
                            //
                            return { 
                                valid: true
                            };
                        },
                        'eventDesc': function (formField) {
                            var eventDescText = eventDescEditor.getData ();
                            
                            if (!!eventDescText) {
                                // the marker shouldn't be considered as text when checking for the presence of text
                                //
                                eventDescText = eventDescText.replace (socialCircle.formHelper._eventEditorsMarker, "");
                            }
                            
                            return {
                                valid: !!eventDescText
                            };
                        },
                        'whatsOnHeaderField': function (formField) {
                            var whatsOnFieldValues      = getWhatsOnFieldValues ();
                            var featureWithinWhatsOn    = $('#featureWithinWhatsOn').attr ('checked');
                            var valid                   = !featureWithinWhatsOn || (
                                                                whatsOnFieldValues.whatsOnHeader != whatsOnDefaults.header && 
                                                                !!whatsOnFieldValues.whatsOnHeader
                                                            );
                            
                            whatsOnHeaderValid = valid;
                            
                            return {
                                valid: valid
                            };
                        },
                        'whatsOnTextField': function (formField) {
                            var whatsOnFieldValues      = getWhatsOnFieldValues ();
                            var featureWithinWhatsOn    = $('#featureWithinWhatsOn').attr ('checked');
                            var valid                   = !featureWithinWhatsOn || (
                                                                whatsOnFieldValues.whatsOnText != whatsOnDefaults.text && 
                                                                !!whatsOnFieldValues.whatsOnText
                                                            );
                            
                            whatsOnTextValid = valid;
                            
                            return {
                                valid:  valid
                            };
                        },
                        'whatsOnFooterField': function (formField) {
                            // always valid - it's an optional field
                            //
                            return {
                                valid: true
                            };
                        }
                    },
                    getValidationHandler: function (formField) {
                        var validationHandler;

                        if (formField.attr ('id') === 'templateName') {
                            validationHandler = function (formField) {
                                return {
                                    valid: isEvent || isTemplateClone || (isNewTemplate && $.trim (formField.val ()) != ""),
                                    message: "Please enter a name for the new template"
                                };
                            };
                        }
                        else if (formField.attr ('id') === 'templates') {
                            validationHandler = function (formField) {
                                return {
                                    valid: !isTemplateClone || formField.val () != "-1",
                                    message: "There are no templates defined"
                                };
                            };
                        }
                        else if (isNewTemplate || isTemplateClone) {
                            validationHandler = function (formField) {
                                return {
                                    valid: true
                                };
                            };
                        }

                        return validationHandler;
                    },
                    getValidationFailureMarkerHandler: function (formField) {
                        var validationMarkerHandler;
                        
                        switch (formField.attr ('name')) {
                        case 'eventDesc':
                            validationMarkerHandler = function (formField, result) {
                                var editorContainer = $('#cke_eventDesc');
                                if (result.valid) {
                                    editorContainer.removeClass ('requiredFieldNeedsAttention');
                                }
                                else {
                                    editorContainer.addClass ('requiredFieldNeedsAttention');
                                } 
                            };
                            break;
                            
                        case 'whatsOnHeader':
                            validationMarkerHandler = function (formField, result) {
                                var note = socialCircle.flashHelper.getMovie ('whatsOn');
                                note.showValidationFailureMarker (!combinedWhatsOnValid ());
                            };
                            break;
                        case 'whatsOnText':
                            validationMarkerHandler = function (formField, result) {
                                var note = socialCircle.flashHelper.getMovie ('whatsOn');
                                note.showValidationFailureMarker (!combinedWhatsOnValid ());
                            };
                            break;
                        case 'whatsOnFooter':
                            // always valid
                            //
                            break;
                        }
                        
                        return validationMarkerHandler;
                    },
                    getValidationFailureHandler: function (formField) {
                        var validationFailureHandler;
                        
                        switch (formField.attr ('name')) {
                        case 'eventDesc':
                            validationFailureHandler = function (formField) {
                                window.setTimeout (function () {
                                    $.scrollTo ($('#cke_eventDesc'), {
                                        duration: 200,
                                        offset: { top: -60, left: 0 },
                                        onAfter: function () {
                                             window.setTimeout (function () { formField.focus (); }, 0);
                                        },
                                        easing: "easeOutExpo"
                                    });
                                }, 0);
                            };
                            break;
                        case 'whatsOnHeader':
                        case 'whatsOnText':
                            validationFailureHandler = function (formField) {
                                if (whatsOnLoaded) {
                                    // do nothing - note is already visible
                                }
                            };
                            break;
                        }
                        
                        return validationFailureHandler;
                    }
                }
            );
        }
    },
    
    
    
    
    
    
    
    
    stringHelper: {
        // socialCircle.stringHelper._re_replaceHtmlTags
        //
        _re_replaceHtmlTags: /\<(?:\/)?([^>]+)\>/,
        
        // socialCircle.stringHelper.replaceHtmlTags (str);
        //
        replaceHtmlTags: function (str) {
            if (str) {
                str = str.replace (socialCircle.stringHelper._re_replaceHtmlTags, "$1");
            }
            return 
        },
        
        // socialCircle.stringHelper.mergeTitleAndDesc (title, desc);
        //
        mergeTitleAndDesc: function (title, desc) {
            var merged;
            
            if (title && desc && title != desc
            ) {
                merged = title + " - " + desc;
            }
            else {
                merged = title || desc;
            }
        
            return merged;
        },
        
        // socialCircle.stringHelper._htmlToTextDomElem
        //
        _htmlToTextDomElem: $(document.createElement ('span')),
        
        // socialCircle.stringHelper.htmlToText (html);
        //
        htmlToText: function (html) {
            var text = socialCircle.stringHelper._htmlToTextDomElem.html (html).text ();
            
            return text;
        },
        
        // socialCircle.stringHelper._re_getUrlFileName
        //
        _re_getUrlFileName: /.*\/([\w-]+)\..*/,
        
        
        // socialCircle.stringHelper.getUrlFileName (url);
        //
        // for an url such as: 'http://www.social-circle.co.uk/default.asp',
        // returns 'default'
        //
        getUrlFileName: function (url) {
            var fileName;
            
            var result = url.match (socialCircle.stringHelper._re_getUrlFileName);
            
            if (result) {
                fileName = result [1];
            }
            return fileName; 
        },
        
        // socialCircle.stringHelper.writtenInteger (value);
        //
        writtenInteger: function (value) {
            var written;
            
            var conv = [
                "zero", 
                "one", "two", "three", "four", "five",
                "six", "seven", "eight", "nine", "ten", 
                "eleven", "twelve", "thirteen", "fourteen", "fifteen",
                "sixteen", "seventeen", "eighteen", "nineteen", "twenty"
            ];
            
            var intVal = Math.round (value);
            if ((value == intVal) && intVal >= 0 && intVal < conv.length) {
                written = conv [intVal];
            }
            else {
                written = value.toString ();
            }
            return written;
        }
    },
    
    linkHelper: {
        reFixNeeded: /http\:\/\/localhost\/social-circle.*/,
        reLinkFix: /^\/(?!social-circle)(.*)/,
            
        // socialCircle.linkHelper.linkFix ();
        //
        linkFix: function () {
            if (window.location.href.match (socialCircle.linkHelper.reFixNeeded)) {
                $("a").each (function () {
                    var a           = $(this);
                    var href        = a.attr ('href');
                    if (href) {
                        var hrefFixed   = href.replace (socialCircle.linkHelper.reLinkFix, "/social-circle/$1");
                        
                        a.attr ('href', hrefFixed);
                    }
                });
            }
        }
    },
    
    adminSettingsHelper: {
        // socialCircle.adminSettingsHelper.write (settingName, settingValue, callback);
        //
        // callback is of the form: function (data, textStatus) {}
        //
        write: function (settingName, settingValue, callback)
        {
            // writes the value; passes the return value to the callback function
            //
            $.post (
                "admin-settings-async.asp", 
                { 
                    settingDirection:   "write",
                    settingName:        settingName, 
                    settingValue:       settingValue 
                },
                callback
            );
        },
        
        // socialCircle.adminSettingsHelper.read (settingName, callback);
        //
        read: function (settingName, callback)
        {
            // read the value; passes the return value to the callback function
            //
            $.post (
                "admin-settings-async.asp", 
                { 
                    settingDirection:   "read",
                    settingName:        settingName
                },
                callback
            );
        }
    },
    
    flashHelper: {
        // var movie = socialCircle.flashHelper.getMovie (movieName);
        //
        getMovie: function (movieName) {
            var isIE = navigator.appName.indexOf ("Microsoft") != -1;
            
            return (isIE) ? window [movieName] : document [movieName];
        }            
    },
    
    domHelper: {
        // socialCircle.domHelper.siteWide ();
        //
        siteWide: function () {
        
            if($.browser.msie) {
                switch ($.browser.version) {
                case "6.0":
                    $('body').addClass ('ie6');
                    break;
                case "7.0":
                    $('body').addClass ('ie7');
                    break;
                case "8.0":
                    $('body').addClass ('ie8');
                    break;
                }
            }
            
            // breaks the browser's ability to recognise links
            //
            //$(document).pngFix(); 
        
// -- misc ----------------------------------------------------------------------------------------
            // make external links open in a new tab/window
            $("a[rel=external]").attr ("target", "_blank");
            
            socialCircle.linkHelper.linkFix ();            
            
// -- drop-down menu ------------------------------------------------------------------------------

            // pre-request the hover images
            //
            var hidden = $(document.createElement ('div'))
                            .css ('display', 'none');

            $(document).append (hidden);
            
            $("#menu > ul > li").each (function () {
                var li = $(this);
                
                var img = $(document.createElement ('img'))
                    .attr ('src', getImageForMenuItem (li, "sel"));
                    
                hidden.append (img);
            });    

            $("#menu > ul > li > ul > li").each (function () {
                $(this).hover (function () {
                    $(this).addClass ("selectedMenuItem");
                }, function () {
                    $(this).removeClass ("selectedMenuItem");
                });
            });
            
            $("#menu > ul > li").hover(function () {
                var li = $(this);
                li.addClass ("openMenu");
                
                setImageForMenuItem (li, "sel");
            },function () {
                var li = $(this);
                li.removeClass ("openMenu");
                
                setImageForMenuItem (li);
            }).click (function (e) {
                var li = $(this);
                if (li.hasClass ("openMenu")) {
                    li.removeClass ("openMenu");
                }
                else {
                    li.addClass ("openMenu");
                }
                var targ = $(e.originalTarget);
                var singleton   = targ.parent() && targ.parent ().parent () && targ.parent().parent ().hasClass ("singleton");
                var topLevel    = targ.parent() && targ.parent().hasClass ("topLevelMenuLink");
                
    //                if (topLevel && !singleton) {
    //                    return false;
    //                }
            });
            
            $("#menu > ul > li").each (function () {
                var li      = $(this);
                var items   = li.find ('li');
                
                if (items.length === 0)  {
                    li.addClass ("singleton");
                }
                li.addClass ("topLevelMenuItem");
                li.children ("a").each (function () {
                    $(this).addClass ("topLevelMenuLink");
                });
            });
            
            var url = window.location.href;
            if (url === '<%= siteRootUrl %>') {
                url += "default.asp";
            }

            // show the current-page indicator
            //
            $('#menu a').each (function () {
                var link    = $(this);
                var linkUrl = link.attr ('href');
                
                if (url.indexOf (linkUrl) != -1) {
                    var li = link.parent ();
                    
                    if (li.hasClass ('topLevelMenuItem')) {
                        setImageForMenuItem (li, "sel", true);
                    }
                    else {
                        li.addClass ('curPage');
                        li = li.parents ('li.topLevelMenuItem');
                        setImageForMenuItem (li, "nested", true);
                    }
                }
            });

            socialCircle.domHelper.replaceTitles ();
            
// -- js-augmented js as IE6 needs to be supported :( ---------------------------------------------

            $('input[type=checkbox]').each (function () {
                $(this).addClass ('checkbox');
            });
            
            $('.leeForm table td:first-child').addClass ('labelCol');
            
            socialCircle.formHelper.augmentCss ();
        },
            
// -- dynamic title images -----------------------------------------------------------------------

        // socialCircle.domHelper._applyDynamicTitleClass ();
        //
        _applyDynamicTitleClass: function (elem, headerSize) {
            var cssClass        = "title";
            var dynamicCssClass = "dynamicTitle";
                switch (headerSize) {
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5:
                    default:
                        cssClass        += headerSize.toString ();
                        dynamicCssClass += headerSize.toString ();
                }
            elem.removeClass (cssClass);
            elem.addClass ("disabledTitle");
            
            dynamicTitle (elem, elem.html (), dynamicCssClass);
        },
        
        // socialCircle.domHelper._re_replaceTitles;
        //
        _re_replaceTitles: /\btitle([0-9]?)\b/,
        
        // socialCircle.domHelper.replaceTitles ();
        //
        replaceTitles: function () {
            $('.title, .title2, .title3, .title4, .title5')
                .not (
                    '.basic, .disabledTitle, .dynamicTitle1, .dynamicTitle2, ' +
                    '.dynamicTitle3, .dynamicTitle4, .dynamicTitle5'
                ).each (
            function () {
            
                var size        = 1;
                var cssClass    = $(this).attr ('class') || "";
                
                var result      = cssClass.match (socialCircle.domHelper._re_replaceTitles);
                
                if (result) {
                    result  = result [1] || "1";
                    size    = parseInt (result);
                }

                socialCircle.domHelper._applyDynamicTitleClass ($(this), size);
            });

            $('.dynamicTitle1:first, .dynamicTitle2:first, .dynamicTitle3:first, .dynamicTitle4:first, .dynamicTitle5:first ').addClass ('first');
        },
        
        // socialCircle.domHelper.applyTooltipDefaults ();
        //
        applyTooltipDefaults: function (settings) {
            var settingsDefaults = {
                track: true, 
                delay: 0, 
                showURL: false, 
                top: -30, 
                left: 15,
                fixPNG: false,
                extraClass: "tooltip"
            };
            
            for (var prop in settingsDefaults) {
                settings [prop] = settingsDefaults [prop];
            }
            
            return settings;
        },
        
        // socialCircle.domHelper.scrollToMainTitle ();
        //
        scrollToMainTitle: function (offset) {
            var params = {
                duration: 200,
                easing: "easeOutExpo"                
            };
            if (offset) {
                params.offset = { top: -60, left: 0 };
            }
            window.setTimeout (function () {
                $.scrollTo ($('#mainTitle'), params);
            }, 250);
        },
        
        // socialCircle.domHelper.scrollToMainTitleWhenReady ();
        //
        scrollToMainTitleWhenReady: function () {
            $(document).ready (function () {
                socialCircle.domHelper.scrollToMainTitle ();
            });
        },
        
        // socialCircle.domHelper._maskedMessageCloseFunc
        //
        _maskedMessageCloseFunc: undefined,
        
        // socialCircle.domHelper.showMaskedProgressIndicator (msg);
        //
        showMaskedProgressIndicator: function (msg) {
            _maskedMessageCloseFunc = $.floatbox({
                content: "<img src='images/progressIndicator3.gif' /><br />" + msg,
                fade: true,
                bgConfig: {
                    zIndex: 1001
                },
                boxConfig: {
                    paddingTop: "18px",
                    paddingBottom: "18px",
                    zIndex: 1002
                },
                button: ""
            });            
        },
        
        // socialCircle.domHelper.hideMaskedProgressIndicator ();
        //
        hideMaskedProgressIndicator: function () {
            if (_maskedMessageCloseFunc) {
                _maskedMessageCloseFunc ();
            }
            _maskedMessageCloseFunc = undefined;
        }
    }
};
