function createForm( element, isModal, successHandler, errorHandler, presubmitHandler, async )
{
    if ( ! element || ! element.attr( 'id' ) ) { return; }
    var num = sg.formIndex++;
    var id = element.attr( 'id' );
    var submitting = false;
    var presubmit = true; // false will prevent form from being submitted
    if ( async === undefined ) async = true;
    //alert('create form, elmt='+element+', id='+id);

    // if we are modal, top into the close event
    // to cleanup
    if ( isModal )
    {
        $(element).bind( 'cbox_closed', function() {
             $("[id^='"+id+"-input']").each( function() {
                var e = $(this);
                var nn = e.get(0).nodeName;
                if ( nn == 'TEXTAREA' ) {
                    e.val('');
                } else if ( nn == 'INPUT' ) {
                    var it = e.attr('type');
                    if ( 'text' == it ) {
                        e.val( '' );
                    } else if ( 'checkbox' == it ) {
                        e.attr( 'checked', false );
                    }
                } else if ( nn == 'SELECT' ) {
                    e.val( '' );
                }
             } );

             $("[id^='"+id+"-error']").each( function() {
                 $(this).css( 'display', 'none' );
             } );

        } );
    }

    // hook up any cancel action
    if ( isModal )
    {
        $('#'+id+'-cancel').click( function( e ) {
            $.fn.colorbox.close();
            e.preventDefault();
        } );
    }

    // hook up the form submit action
    var submitFunction = function() {
        //alert('submit');
        if ( submitting ) return;
        
        // call presubmit
        if ( presubmitHandler ) {
        	presubmit = presubmitHandler();
        	if ( ! presubmit ) return;
        }
        
        submitting = true;
        // hide errors
        $("[id^='"+id+"-error']").each( function() {
            $(this).css( 'display', 'none' );
        } );
        $('#'+id+'-success').css('display','none');
        // show loading
        $('#'+id+'-loading').css( 'display', 'block' );
        if ( isModal ) {
            $.fn.colorbox.resize();
        }

        // submit
        $('#'+id).ajaxSubmit( {
            dataType: 'json',
            async: async,
            success: function( results ) {
             //alert(results.returnCode);
                $('#'+id+'-loading').css( 'display', 'none' );
                if ( results.returnCode == 200 ) 
                {
                    // success
                    if ( successHandler ) {
                        successHandler( results );
                    }

                    if ( results.message ) {
                        showClientStatus( results.message );
                    }

                    if (results.data && results.data.keepAlive && results.data.keepAlive == true) {
                    	var doClose = false;
                    } else {
                    	var doClose = true;
                    }
                    
                    if ( results.redirect && results.redirect != 'NONE' ) {
                        if ( results.redirect == 'RELOAD' ) {
                        	location.reload(true);
                        	//$(location).attr( 'href', $(location).attr('href') );
                        } else {
                            if ( results.data && results.data.redirectMode && 
                                    results.data.redirectMode == 'inline' )
                            {
                                doClose = false;
                                $.fn.colorbox( {
                                    opacity: .6,
                                    scrolling: false,
                                    href: results.redirect,
                                    inline: results.data.inlineHref ? true : false,
                                    open: true
                                } );
                            }
                            else
                            {
                                $(location).attr( 'href', results.redirect );
                            }
                        }
                    } else if ( results.redirect == 'NONE' ) {
                        doClose = false;
                    }
                    if ( results.inPlaceMessage ) {
                        $('#'+id+'-success').html(results.inPlaceMessage);
                        $('#'+id+'-success').css('display','block');
                    }
                    else if ( isModal && doClose ) {
                        $.fn.colorbox.close();
                    }
                }
                else
                {
                	errorReturn = true;
                	if ( errorHandler ) {
                		errorReturn = errorHandler( results );
                	}

                	if ( errorReturn )
                	{
	                    // handle error
	                    $('#'+id+'-error').html( results.message );
	                    //alert('error, looking for: '+'#'+id+'-error'+ '('+$('#'+id+'-error').length+')');
	                    $('#'+id+'-error').css( 'display', 'block' );
	                    for ( field in results.errors ) 
	                    {
	                        var eelmt = $('#'+id+'-error-'+field);
	                        if ( eelmt ) {
	                            eelmt.html( results.errors[ field ] );
	                            eelmt.css( 'display', 'block' );
	                        }
	                    }
	                    if ( isModal ) {
	                        $.fn.colorbox.resize();
	                    }
                	}
                }
                submitting = false;
            }
        } );
    }; 


    $(element).bind('submit', function() {
        submitFunction();
        return false;
    } );
    
    $('#'+id+'-submit').click( function( e ) {
        submitFunction();
        if ( e ) {
            e.preventDefault();
        }
    } );
    $('.'+id+'-submit').click( function( e ) {
        submitFunction();
        if ( e ) {
            e.preventDefault();
        }
    } );

}

function showClientStatus( message )
{
    if ( sg.statusTimeout ) {
        clearTimeout( sg.statusTimeout );
    }
    $('#clientStatus').html( message );
    $('#clientStatus').css( 'display', 'block' );
    sg.statusTimeout = setTimeout( "$('#clientStatus').hide('normal');", sg.statusLengthMs );
}

function dateDiff( start, end )
{
    if ( ! start || ! end ) { return null; }
    var diff = Math.ceil( ( end.getTime() - start.getTime() ) / (24*60*60*1000) );
//    alert('diff '+start+'('+sd+') - '+end+' = '+diff);
    return diff;
}

function parseDate( dstr )
{
    if ( ! dstr ) { return null; }
    var parts = dstr.split( '/' );
    if ( ! parts || parts.length != 3 ) { return null; }
    var d = new Date( parts[2], (parseInt(parts[0],10)+12-1)%12, parts[1] );
    return d;
}

function dateToPickString( d )
{
    if ( ! d ) { return null; }
    var mon = d.getMonth()+1;
    var day = d.getDate();
    var year = d.getFullYear();

    var ms = mon < 10 ? '0'+mon : ''+mon;
    var ds = day < 10 ? '0'+day : ''+day;
    var ys = ''+year;

    return ms+'/'+ds+'/'+ys;
}

function getObjectID(elt_id)
{
	if (!elt_id) return false;
	return elt_id.substring(elt_id.lastIndexOf("-") + 1);
}

function IsNumeric(val) 
{
	if (isNaN(parseFloat(val))) {
          return false;
	}
    
	return true
}

function getParam(name, url)
{
	url = url || window.location.href;
	name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( url );
    if( results == null )
        return "";
    else
    return results[1];
}

function replaceParam(key, val, url)
{
	key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
	var urlString = url || window.location.href;
    var regexS = "[\\?&]"+key+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( window.location.href );
    if( results == null ) {
        return urlString + '&' + key + '=' + val;
    } else {
    	var paramString = results[0];
    	var newParamString = paramString.substring(0, paramString.search('=') + 1) + val;
    	urlString = urlString.replace(paramString, newParamString);
    	return urlString;
    }
}

function roundNumber(num, dec)
{
	var precise = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return formatCurrency(precise);
}

function formatCurrency(num)
{
	num = num.toString().replace(/\$|\,/g,'');
	
	if (isNaN(num)) {
		num = "0";
	}
	
	sign = (num == (num = Math.abs(num)));
	num = Math.floor(num*100+0.50000000001);
	cents = num%100;
	num = Math.floor(num/100).toString();
	
	if (cents<10) {
		cents = "0" + cents;
	}
	
	for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++) {
		num = num.substring(0,num.length-(4*i+3))+','+num.substring(num.length-(4*i+3));
	}
	
	return (((sign)?'':'-') + num + '.' + cents);
}

function Left(str, n)
{
	if (n <= 0)
        return "";
    else if (n > String(str).length)
        return str;
    else
        return String(str).substring(0,n);
}

function Right(str, n)
{
    if (n <= 0)
        return "";
    else if (n > String(str).length)
        return str;
    else {
        var iLen = String(str).length;
        return String(str).substring(iLen, iLen - n);
    }
}

function addDays( d, numDays ) 
{
    if ( ! d ) { return null; }
    return new Date( d.getTime() + (numDays*(24*60*60*1000)) );
}

/**
 * Cookie methods
 */
function setCookie(c_name, value, expiredays)
{
	var exdate=new Date();
	exdate.setDate(exdate.getDate()+expiredays);
	document.cookie=c_name+ "=" +escape(value)+";path=/"+
		((expiredays==null) ? "" : ";expires="+exdate.toUTCString());
}

function getCookie(c_name)
{
	if (document.cookie.length>0) {
		c_start=document.cookie.indexOf(c_name + "=");
		if (c_start!=-1) {
			c_start=c_start + c_name.length+1;
			c_end=document.cookie.indexOf(";",c_start);
			if (c_end==-1) c_end=document.cookie.length;
			return unescape(document.cookie.substring(c_start,c_end));
		}
	}
	return "";
}

$(document).ready( function() 
{
    // color box cancel event
    $('.colorbox_cancel').click( function(e) {
        $.fn.colorbox.close();
        return false;
    });
    
    // signup redirect modal
    $('.signupRedirect').click( function(e) {
    	if (sg.signupRedirectUrl) {
    		$(location).attr('href', sg.signupRedirectUrl );
    	}
    	e.preventDefault();
    });
    
    // tooltips
    $('.tooltip-link').each(function () {
        // options
        var time = 250;
        var hideDelay = 1000;
        var hideDelayTimer = null;

        // tracker
        var beingShown = false;
        var shown = false;
        
        var trigger = $('.trigger', this);
        var width = $('.offset-width', this).val();
        var height = ($('.offset-height', this).length) ? $('.offset-height', this).val() : 0;
        var popup = $('.tooltip', this).css('left', width + 'px').css('top', height + 'px');
        
        // set the click and mouseout on both element
        $([trigger.get(0), popup.get(0)]).click(function () {
        	// stops the hide event if we move from the trigger to the popup element
        	if (hideDelayTimer) clearTimeout(hideDelayTimer);

        	// don't trigger the animation again if we're being shown, or already visible
        	if (beingShown || shown) {
        		return;
        	} else {
        		popup.css('display', 'block');
        		beingShown = false;
        		shown = true;
        	}
        }).mouseout(function () {
        	// reset the timer if we get fired again - avoids double animations
        	if (hideDelayTimer) clearTimeout(hideDelayTimer);
          
        	// store the timer so that it can be cleared in the mouseover if required
        	hideDelayTimer = setTimeout(function () {
        		hideDelayTimer = null;
        		shown = false;
        		popup.css('display', 'none');
        	}, hideDelay);
        }); // click + mouseout
        
        // set the mouseover for the popup
        $([popup.get(0)]).mouseover(function () {
        	// stops the hide event if we move from the trigger to the popup element
        	if (hideDelayTimer) clearTimeout(hideDelayTimer);

        	// don't trigger the animation again if we're being shown, or already visible
        	if (beingShown || shown) {
        		return;
        	} else {
        		popup.css('display', 'block');
        		beingShown = false;
        		shown = true;
        	}
        }); // mouseover
    }); // each
        
    //$('.tooltip-link').click( function(e) {
    //	width = $(this).width();
    //	tooltip = $(this).find('.tooltip');
    //	tooltip.css({ 'left' : function(){ return width + 17; } });
    //	tooltip.show();
    //});
});

$.fn.imagesLoaded = function(callback){
  var elems = this.filter('img'),
      len = elems.length;
      
  elems.bind('load',function(){
      if (--len <= 0){ callback.call(elems,this); }
  }).each(function(){
     // cached images don't fire load sometimes, so we reset src.
     if (this.complete || this.complete === undefined){
        var src = this.src;
        // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
        // data uri bypasses webkit log warning (thx doug jones)
        this.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
        this.src = src;
     }
  });

  return this;
};

/*
 * ScrollToElement 1.0
 * Copyright (c) 2009 Lauri Huovila, Neovica Oy
 *  lauri.huovila@neovica.fi
 *  http://www.neovica.fi
 *  
 * Dual licensed under the MIT and GPL licenses.
 */

(function($) {
    $.scrollToElement = function( $element, speed ) {

        speed = speed || 750;

        $("html, body").animate({
            scrollTop: $element.offset().top,
            scrollLeft: $element.offset().left
        }, speed);
        return $element;
    };

    $.fn.scrollTo = function( speed ) {
        speed = speed || "normal";
        return $.scrollToElement( this, speed );
    };
})(jQuery);

/*
 * Coda bubbles
 */
$(function () {
	createBubbles();
});
function createBubbles()
{
    $('.bubbleInfo').each(function () {
        var distance = 00;
        var time = 250;
        var hideDelay = 500;

        var hideDelayTimer = null;

        var beingShown = false;
        var shown = false;
        var trigger = $('.trigger', this);
        var info = $('.popup', this).css('opacity', 0);


        $([trigger.get(0), info.get(0)]).mouseover(function () {
            if (hideDelayTimer) clearTimeout(hideDelayTimer);
            if (beingShown || shown) {
                // don't trigger the animation again
                return;
            } else {
                // reset position of info box
                beingShown = true;

                info.css({
                    bottom: -5,
                    left: -2,
                    display: 'block'
                }).animate({
                    bottom: '+=' + distance + 'px',
                    opacity: 1
                }, time, 'swing', function() {
                    beingShown = false;
                    shown = true;
                });
            }

            return false;
        }).mouseout(function () {
            if (hideDelayTimer) clearTimeout(hideDelayTimer);
            hideDelayTimer = setTimeout(function () {
                hideDelayTimer = null;
                info.animate({
                    bottom: '+=' + distance + 'px',
                    opacity: 0
                }, time, 'swing', function () {
                    shown = false;
                    info.css('display', 'none');
                });

            }, hideDelay);

            return false;
        });
    });
}

function printStackTrace() {
  var callstack = [];
  var isCallstackPopulated = false;
  try {
    i.dont.exist+=0; //doesn't exist- that's the point
  } catch(e) {
    if (e.stack) { //Firefox
      var lines = e.stack.split('\n');
      for (var i=0, len=lines.length; i<len; i++) {
        if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
          callstack.push(lines[i]);
        }
      }
      //Remove call to printStackTrace()
      callstack.shift();
      isCallstackPopulated = true;
    }
    else if (window.opera && e.message) { //Opera
      var lines = e.message.split('\n');
      for (var i=0, len=lines.length; i<len; i++) {
        if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
          var entry = lines[i];
          //Append next line also since it has the file info
          if (lines[i+1]) {
            entry += " at " + lines[i+1];
            i++;
          }
          callstack.push(entry);
        }
      }
      //Remove call to printStackTrace()
      callstack.shift();
      isCallstackPopulated = true;
    }
  }
  if (!isCallstackPopulated) { //IE and Safari
    var currentFunction = arguments.callee.caller;
    while (currentFunction) {
      var fn = currentFunction.toString();
      var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
      callstack.push(fname);
      currentFunction = currentFunction.caller;
    }
  }
  output(callstack);
}

function output(arr) {
  //Optput however you want
  alert(arr.join('\n\n'));
}

function urlAppend( url, args ) 
{
    var connector = '&';
    if ( url && url.indexOf( '?' ) == -1 ) {
        connector = '?';
    } else if ( ! url ) {
        url = '';
    }
    return url + connector + args;
}

function urlStrip( url, exclude )
{
    var newUrl = url;
    var parts = url.split('?', 2);
    if ( parts.length > 1 && parts[1] )
    {
        var base = parts[0];
        var newArgs = '';
        var first = true;
        var sparts = parts[1].split('&');
        for ( var i = 0; i < sparts.length; i++ )
        {
            var kvparts = sparts[i].split('=', 2);
            if (kvparts && kvparts.length > 0 && kvparts[0] == exclude ) {
                continue;
            }
            else {
                if ( ! first ) newArgs += '&'; else first = false;
                newArgs += kvparts[0];
                if ( kvparts.length > 1 ) newArgs += ( '=' + kvparts[1] );
            }
        }
        if ( newArgs ) {
            newUrl = base + '?' + newArgs;
        } else {
            newUrl = base;
        }
    }
    return newUrl;
}


function urlRootRelative( url )
{
    if ( ! url || url.indexOf( 'http' ) != 0 ) { return url; }
    newUrl = url.substring( url.indexOf( '/', 7 ) );
    //alert('root rel '+url+' = '+newUrl);
    return newUrl;
}

function restoreContext()
{
    $.post( 
        sg.rootpath+'/meta/restorecontextcallback', 
        {},
        function (results) {
            if ( results && 200 == results.returnCode && results.data && results.data.context ) 
            {
                var loc = $(location);
                var rrUrl = urlRootRelative( loc.attr('href') );
                var curUrl = urlStrip( rrUrl, 'cId' );
                if ( curUrl != results.data.context.url ) {
                    //alert('reloading context: current='+curUrl+', new='+results.data.context.url);
                    var url = results.data.context.url;
                    url = urlAppend( url, 'cId='+results.data.context.id );
                    loc.attr('href', url );
                } else {
                    //alert('raising local context update');
                    $(document).trigger('sg_context_update', [results.data.context.modal, results.data.context.args]);
                }
            }
        },
        'json'
    );
}

function pushContext( context )
{
    if ( context && ( context.url || context.modal ) )
    {
        $.post( 
            sg.rootpath+'/meta/pushcontextcallback', 
            {
                cUrl: context.url,
                cModal: context.modal,
                cArgs: context.args
            },
            function (results) {},
            'json'
        );
    }
}


function showModal( name, restore )
{
    //alert('showModal: '+name);
    args = {
        opacity: .6,
        inline: true,
        href: '#'+name,
        show: true,
        scrolling: false 
    };

    if ( restore ) {
        args[ 'onClosed' ] = restoreContext;
    }
    $.fn.colorbox( args );
}

