//
/* ----------------------------------------------------------------

OkCupid Utilities file

	- usefulish stuff
	
This file contains general utilities used through the site

---------------------------------------------------------------- */

var Utilities = {
	
	/* Browser Functioning ------------------------------------------------------------------------ */
	
	handleEnter:function(e,f) //Needs testing
	{
		/* 
		Important: f cannot return ANYTHING.  No returns can be 
		made except the negated keyCode
		*/
		f = f || function (){};
		var Ucode=e.keyCode? e.keyCode : e.charCode;
		if (Ucode == 13){f();}
		e = (e) ? e : ((window.event) ? window.event : "");
		if (e) {return !( e.keyCode==13 || e.which==13 );}
	},
	preloadImages:function() 
	{
		var preload_arr = new Array();
		for(var i = 0; i < arguments.length; i++) {
			var preload_img = new Image();
			preload_img.src = arguments[i];
			preload_arr.push(preload_img);
		}
	},
	
	/* String functions --------------------------------------------------------------------------- */
	
	quickTrim:function(str) 
	{
        return str.replace(/(^\s+)([^\s]*)(\s+$)/, '$2');
    },
	stripBreaks:function(str) 
	{
		var res = str.replace(/\n/g," ");  	// ??
		res = res.replace(/\n/g," ");		// ??
		return res;
	},
	stripComments:function(str) 
	{
	    str = str.replace(/<!--[\w\s\/\-,]*-->/g, " ");
	    return str;
	},
    jogf:function(string,key)
    {
		// IE needs the var
        for(var item in key)
            string = string.replace("%" + item,key[item]);
        return string;
    },
    
	/* Math functions ----------------------------------------------------------------------------- */
	
	leadZero:function(n) {
		if (n < 10) n = "0" + n;
		return n;
	},
	formatBigInteger:function(integer) // Can this be simplified?
	{
	    var result = '',
	    	pattern = "###,###,###,###";

	    // cast that to a string.
	    integer = "" + integer + "";

	    integerIndex = integer.length - 1;
	    patternIndex = pattern.length - 1;

	    while ( (integerIndex >= 0) && (patternIndex >= 0) )
	    {
	        var digit = integer.charAt( integerIndex );
	        integerIndex--;

	        // Skip non-digits from the source integer (eradicate current formatting).
	        if ( (digit < '0') || (digit > '9') )  continue;

	        // Got a digit from the integer, now plug it into the pattern.
	        while ( patternIndex >= 0 )
	        {
	            var patternChar = pattern.charAt( patternIndex );
	            patternIndex--;

	            // Substitute digits for '#' chars, treat other chars literally.
	            if ( patternChar == '#' )
	            {
	                result = digit + result;
	                break;
	            }
	            else
	            {
	                result = patternChar + result;
	            }
	        }
	    }
	    return result;
	},
	
	/* Array Functions ---------------------------------------------------------------------------- */
	
	// note: currently doubled up below for backwards compatibility
	
	pushFront:function(A, x, maxsize) 
	{
	 	var i=A.length;
	 	if (maxsize && i>maxsize - 1)
	  	i=maxsize - 1;
	 	while (i>0) {
	  		A[i] = A[i-1];
	  		i--;
	 	}
	 	A[0] = x;
	},

	popMiddle:function(A,i) 
	{
	  	var x;
	  	if (i>A.length-1) return (false);
	  	for (x=i; x<A.length-1; x++) A[x]=A[x+1];
	  	A.length=A.length-1;
	  	return (true);
	},

	popFront:function(A) 
	{
	  	return (popMiddle(A,0));
	},

	moveToFront:function(A,i) 
	{
	 	var temp=A[i];
	 	while (i>0) {
	  		A[i]=A[i-1];
	  		i--;
	 	}
	 	A[0]=temp;
	},
	
	
	/* Interface ---------------------------------------------------------------------------------- */

	// Takes a form and wings it to a service.  Dependencies underway.  Localize needs a little more thought
	jackForm:function(form, url, success, dependent, localize)
	{
	    
		this.parameters = $(form).serialize();
		this.success = success;
		
		var pass = this;
				
		new Ajax.Request(url,
			{
				parameters:pass.parameters,
				method:"post",
				onSuccess:pass.success
			}
		);
	},

	setDefaultText:function(objt,text) //browse usage, may be more automated, drop dual calls, maybe action bind
	{
		var obj = objt,
			txt = text;
			
		util.doOnDomLoad(
				function() {
					if ($(obj)) {
						obj = $(obj);
						if (obj.value == '') 
							obj.value = txt;
						obj.observe("focus", function(){
							if (obj.value == txt) 
								obj.value = '';
							obj.addClassName('focus');
						});
						obj.observe("blur", function(){
							if (obj.value == '') 
								obj.value = txt;
							obj.removeClassName('focus');
						});
					}
				}
		);
	},
	
	/*
	Toggle has become fairly complex, so I'm stopping the feat creep here.  
	If you need to expand on it, or it's not doing what it should, let me
	know.  If it gets any bulkier its going to need to be broken down into
	more functions. Check the documentation.
	*/
	toggle_set:new Array(),
	triggers:new Array(),
	toggle:function(obj,bit,destiny,set,trig_obj)
	{
		var element = $(obj),
			display = element.getStyle("display"),
			trigger;
		
		/* 
		The below code is a bit ugly, but not wrong.  
		Dislike nested ifs, but that's sort of the 
		way these animated funcs work themsleves out.

		Will tackle it another time - PW 
		*/
		
		if(trig_obj) 
			trigger = $(trig_obj);
		else 
			trigger = false;
		
		if(set || set == 0) {
			if(typeof(this.toggle_set[set])=="undefined") this.toggle_set[set] = new Array();
			this.toggle_set[set][this.toggle_set[set].length] = element;
		}

		destiny = (destiny ? destiny : "block");
		if (bit) $bit.toggle(bit);
		
		if (display == "none") {
			if(set || set == 0) for(iter=0;iter<this.toggle_set[set].length;++iter) this.toggle_set[set][iter].style.display = "none";
			element.style.display = destiny;
		}
		else element.style.display = "none";
		
		if(trigger && (set || set == 0)) {
			if(typeof(this.triggers[set]) == "undefined") this.triggers[set] = new Array();
			this.triggers[set][this.triggers[set].length] = trigger;
		}
		
		if((set || set == 0) && typeof(this.triggers[set]) != "undefined") {
			for(iter=0;iter<this.triggers[set].length;++iter) this.triggers[set][iter].removeClassName("active");

			if (display == "none" && trigger != false) 
				trigger.addClassName("active");
			else if(trigger != false)
				trigger.removeClassName("active");
		}
	},

	/*  */

	randomQuip:function(lines,returnit)
	{
	    pickquip = Math.round(Math.random()*(lines.length-1));
	    if(!returnit)
	        document.write(lines[pickquip]);
        else
            return lines[pickquip];
	},
	

	/*
	This function needs some work.
	*/
	buddyCallWrapper:function(params,addRemove,notify,optmessage) {
		params.ajax = 1;
		new Ajax.Request("/profile", {
			parameters:params,
			onSuccess:function(response) {
				var text = "";
				switch(parseInt(response.responseText)){
					case 0:
						text = "User has been removed from your favorites.";
						if(addRemove == 1) text = (notify ? "User saved! They've been sent a message to let them know they're one of your favorites." : "User Saved!");
						break;
					case 12: text = "User saved! They've been sent a message to let them know they're one of your favorites."; break;
					default: text = "Couldn't save for some reason. Sorry. Try later."; break;
				}
				var target = (optmessage ? optmessage : 'save_buttons');
				if($(target)) $(target).innerHTML = '<a class="buddy_removed" href="#nogo">'+text+'</a>';
				if (Mailbox && Mailbox.m_current_thread) Mailbox.addSystemMessage ('buddy', text);
			}
		});
	},


	// Returns the position we should scroll to if we want to
	// align the top of the element with the top of the viewport
	getYPosTop: function (el) {
		return $(el).cumulativeOffset().top;
	},
	
	// Returns the position we should scroll to if we want to align
	// the bottom of the element with the bottom of the viewport
	getYPosBottom: function (el) {
		el = $(el);
		return this.getYPosTop(el) + el.getDimensions().height - document.viewport.getDimensions().height;
	},
	
	// Vars for smooth scrolling
	m_scroll_interval: null,
	m_scroll_difference: 0,
	m_scroll_times: 0,
	
	// Find the difference between the current Y and the target Y.  Call a function once every 10 ms
	// that will move the screen (1/speed)*difference closer to the target Y.  Lower speed == faster.
	smoothScroll: function (new_ypos, speed, linear) {
		this.m_scroll_difference = new_ypos - document.viewport.getScrollOffsets().top;
		this.m_scroll_times = speed;
		this.m_scroll_interval = setInterval ('util.smoothIteration(' + new_ypos + ', ' + speed + ', ' + (linear ? 1 : 0) + ')', 10);
	},
	
	// Scroll (1/speed)*original_difference closer to the target Y (linear).
	// Scroll (1/speed)*remaining_difference closer to the target Y (elastic).
	smoothIteration: function (new_ypos, speed, linear) {
		if (linear) {
			var amount_to_move = this.m_scroll_difference/speed;
		}
		else {
			var amount_to_move = Math.ceil(this.m_scroll_difference/speed);	
			this.m_scroll_difference -= amount_to_move;
		}
		if (amount_to_move == 0 || linear && this.m_scroll_times-- < 1)
			clearInterval (this.m_scroll_interval);
		else
			scroll (0, document.viewport.getScrollOffsets().top + amount_to_move);
	},
	
	/* Site specific ------------------------------------------------------------------------------ */


    // 
    
    updateStats: function(name, value, type, hash, optional_params) {
         new Ajax.Request("/poststat",{
             method : "get",
             parameters : {"name" : name, "value" : value, "type" : type, "hash" : hash, "rnd" : Math.random()},
             onSuccess  : util.updateStats_cb.bindAsEventListener(util, optional_params)
             });
    },
    
    updateStats_cb : function(transport, optional_params) {
               
         var res = transport.responseText.evalJSON();
         if (res.error) {
             alert("Stat posting error" + res.error);
         }
 	 if (optional_params && optional_params["cb"]) {
	     optional_params.cb();
         }
         if (optional_params && optional_params["redirect_to"]) {
             window.location.href = optional_params["redirect_to"];
         }
    },

    
	checkNonbotAjax:function(url) // may move this to profile if it has no other use
	{
		jax = new Ajax.Request(
			url,
			{
				method: "get",
				parameters: {"nonbot": 1},
				onSuccess: function(){},
				onFailure: function(){}
			}
		);
		return true;//jax;  // why pass object?
	},

    printOptimateAd: function() {
         var optimates = ["business","green","technology","entertainment","relationships","astrology"];
         var i = Math.floor(Math.random() * optimates.length);
         var choice = optimates[i];
         $("optimate-ad").innerHTML = '<a target="new" href="/ads3?adClicked='+(i+10)+'&sourcePage=unavailable&sourceViewer=unavailable&redirect=/adcode/optimate_' + choice + '.html">Here\'s something random: <strong>'
         + choice + ' movie</strong></a>';
    },
    
	displayAdvert:function(position,keywords,isloggedin,slot_name)
	{

	    slot_name = slot_name || "Other";
	
		switch(position)
		{
			case "sky" 	: slot_addon = "Sky"; break;
			case "LB" 	: slot_addon = "Ldr"; break;
			case "rect" : slot_addon = "Box"; break;
		}
	
	    if (position != "transitional") { slot_name = slot_name + "_" +  slot_addon;}

	    if (position == "Left" || position == "sky") {
	        width = 160; height = 600;
	    } else if (position == "Top" || position == "LB") {
	        width = 728; height = 90;
	    } else if (position == "Middle" || position == "rect") {
	        width = 300; height = 250;
	    } else if (position == "Right3") {
	        width = 200; height = 500;
	    } else if (position == "Transitional" || position == "transitional") {
	        width = 500; height = 500;
		} else {
	        width = 40; height = 40;
	    }
	    
	    // Width and height added to url to pass cgi to ZEDO
	    var url = "http://ads.okcimg.com/google/ad_manager?ad_slot="+slot_name+"&keywords="+keywords+"&pass_height="+height+"&pass_width="+width;

	    document.write("<iframe id=\"ad_frame_"+position+"\"  width=" + width + " height=" + height + 
	            " marginwidth=0 marginheight=0 hspace=0 vspace=0" + 
	            " frameborder=0 scrolling=no bordercolor=\"#000000\"" +
	            " src=\"" + url + "\"></iframe>");
	},

		clickLang:function(code, loggedin) 
		{
			var temp=window.location.href,
					dest=temp;
			
			if (temp.indexOf(".com") != -1) {
				temp=temp.substring(temp.indexOf(".com"));
				
				if (temp.indexOf("/") != -1) {
					temp=temp.substring(temp.indexOf("/"));
				}
				if (temp.length > 3) {
					dest = temp;
					dest = encodeURIComponent(dest);
				} 
				else {
					dest = "/";
				}
			}
			
			// if we're at the office, test with an ajax call
			if ( document.location.href.indexOf("malf") == -1 && document.location.href.indexOf(":2000") != -1) {
				dest = "/settings";
				params = { set_lang: code };
				
				var jax = new Ajax.Request(dest, {
					parameters: params,
					onSuccess: function(){document.location.reload(true);},
					onFailure: function(){document.location.reload(true);}
				});
			}
			else {
				dest = "/settings?p=" + dest + "&set_lang=" + code;
				document.location.href = dest;
			}
		},

    ads3_call:function(id, params) {
        
        if(GOOGLE_PUNCH == true) {
            return;
        }

        var get_src = window.location.toString();
        var parseit = get_src.substring(get_src.indexOf("/",7));
        if(parseit.indexOf("?") != -1) parseit = parseit.substring(0,parseit.indexOf("?"));

        params += "&sourcePage=" + parseit;
        params += "&section=" + id;



        new Ajax.Request(
            "/ads3",
            {
                method:'get',
                parameters: params,
                evalScripts: true,
                onSuccess:function(response)
                {
                    if(id=='ad_set_top_profile' && response.responseText.indexOf("good_to_show") != -1)
                    {
                        $('topset').style.background = "#CDD9E6";
                        $('top_cycle').style.display = "block";
                        $('ad_set_top_profile').innerHTML = response.responseText;
                    } else {
                        $('topset').style.display = "none";
                    }   
                }
            }
        );
     },
     
     // Moved back here because we'll have to flip for non-admin accounts
     flipDesign:function() {
         var c = getCookie("redesign");
         if (c && c == "1") {
	     util.updateStats("ui - flip from redesign", 1, "counter", "usxK7602Ep+KxarPcj+Cs4vm3YA=");
                setCookie("redesign","0",secondsFromNow(86400*30));
         }
         else {
	     util.updateStats("ui - flip to redesign", 1, "counter", "N+mQqECk84TokAOpPNLkOJ0glcs=");
                setCookie("redesign","1",secondsFromNow(86400*30));
         }
	 setTimeout("document.location.reload();",500);
     },

	
	/* Fixes the left_bar/main_content height problem ------------------------ */

	adjustMCHeight:function() {
		// none of this matters if the left_bar isn't present
		//
		if ($('left_bar')) {
			var lb_height = $('left_bar').getHeight() + 10; // 10px for margin
			var mc_height = $('main_content').getHeight();
		
			if (!$('hold_mc_height')) {
				// create the hidden input to hold previous size
				var hold_mc_height = '<p class="hidden"><input type="hidden" name="hold_mc_height" value="'+ mc_height +'" id="hold_mc_height" /><\/p>';
			
				// insert it into the bottom of the footer
				$('footer').insert(hold_mc_height, { position: "bottom" });
			}
			else if (lb_height < $F('hold_mc_height')) {					
			  if(BrowserDetect.browser =="Explorer" && BrowserDetect.version == 6)
				    $('main_content').style.height = $F('hold_mc_height') + "px";
				else 
				    $('main_content').style.minHeight = $F('hold_mc_height') + "px";
			}
		
			if (lb_height > mc_height) {
			    if(BrowserDetect.browser =="Explorer" && BrowserDetect.version == 6)
				    $('main_content').style.height = lb_height + "px";
				else 
				    $('main_content').style.minHeight = lb_height + "px";
				
			}
		}
	},

	update_buzz_topic:function(topic)
	{
		$('set_topic_btn').style.display = "none";
		$('set_topic_spinner').style.display = "inline";
		
		$('lb_set_chat_input').style.background = "#1E3F66";
		$('lb_set_chat_input').style.color = "#325279";
		
		new Ajax.Request(
			"/instantevents",
			{
				method:"get",
				parameters:{
					buzz_topic:topic,
					"im.add_topic_ajax":1
				},
				onSuccess:function(response)
				{
					$('lb_set_chat_input').disabled = true;
					$('set_topic_success').style.display = "inline";
					$('set_topic_spinner').style.display = "none";
					
					if($("topic_name"))
						$("topic_name").value = $('lb_set_chat_input').value;
				}
			}
		);
	},
    
	/* IE fixes ----------------------------------------------------------------------------------- */
	
	IE6Hover:function(element_id, get_li, get_lis)
	{ 
		// pass get_lis = true to get the element's list items
		// pass get_li = true to get the element's first list item

		var parent = document.getElementById(element_id);
		var element = (get_lis) ? parent.getElementsByTagName('li') : ((get_li) ? parent.getElementsByTagName('li')[0] : parent);
	
		// in order for the this keyword to actually work, we can't use the attachEvent() method
		// as this in that context refers to the window and not the object that called the func
	
		function addClassName() { this.className += " hover"; };
		function removeClassName() { this.className = this.className.replace(new RegExp(" hover\\b"), ""); };
	
		if (get_lis) {
			for(var i = 0; i < element.length; i++) {
				element[i].onmouseover = addClassName;
				element[i].onmouseout = removeClassName;
			}
		}
		else {
			element.onmouseover = addClassName;
			element.onmouseout = removeClassName;
		}	
	},
    
	// adds/removes a faux :focus with a class name of focus

	focusFix:function(element_id)
	{
		var element = $(element_id);
		
		element.onfocus = function () {			
			element.className += " focus";
		};
	
		element.onblur = function () {
			element.className = element.className.replace(new RegExp(" focus\\b"), "");
		};
	},

	// all JS fixes for IEs 6 and 7 go in here	
	hoverfixes:new Array(),
	windowsFixes:function()
	{
	    
		if(BrowserDetect.OS == "Windows") {
		    
			if ($('footer_language_nav')) { $('footer_language_nav').style.letterSpacing = "-1px"; }
			if ($('site_search_query')) { util.focusFix('site_search_query'); }
			if ($('header_username')) { util.focusFix('header_username'); }
			if ($('header_password')) { util.focusFix('header_password'); }
	        
			if (BrowserDetect.browser == "Explorer" && BrowserDetect.version == "6") {
			    
				if($('header')) util.IE6Hover('header');
				if($('quicklinks')) util.IE6Hover('quicklinks');
				if($('lang_trigger')) util.IE6Hover('lang_trigger');
				if($('language_nav')) util.IE6Hover('language_nav', true);
				if($('submit-button')) util.IE6Hover('submit-button');
				if($('side-submit-button')) util.IE6Hover('side-submit-button');

				// need to clear this up
				if($('site_nav')) util.IE6Hover('site_nav', false, true);
				if($('site-nav')) util.IE6Hover('site-nav', false, true);
				
				if(this.hoverfixes)
				    for(iter=0;iter<this.hoverfixes;++iter)
				        if($(this.hoverfixes[iter])) 
				            this.IE6Hover(this.hoverfixes[iter]);
				
			}	
		}
	},
	
	// because IE seems to only like document.getElementsByClassName() and
	// not element.getElementsByClassName()
	//
	getElementsByClassName:function(parent, class_name)
	{
		var o = parent.getElementsByTagName('*'); // get all elements under parent
		var a = new Array();
	
		for (var i = 0; i < o.length; i++) {
			// does the element have the class attribute and does class_name exist 
			if (o[i].attributes["class"] && o[i].attributes["class"].value.toString().indexOf(class_name) != -1) {
				a.push(o[i]); // push elements onto array
			}
		}
	
		return a;
	},
	
	toggleSavedProfiles:function(id)
	{
		var saved_display = NanoCookie.get('saved_display');
		
		// toggle the section
		this.toggle(id, 32);
		
		// set the cookie for 2 hours if it isn't set
		if (!saved_display) NanoCookie.set('saved_display', '1', {ms: 7200*1000});
	},
    
    // 
    uidBit: function(uid_str, which_bit) {
        if (which_bit < 1 || which_bit > 8) alert ("Whoa; UID bit function works 1..8");
        var tail = uid_str.substr(uid_str.length - 9);
        while(tail.charAt(0) == "0") {
            tail = tail.substr(1);
        }
        var num = parseInt(tail);
        if (num & Math.pow(2,(which_bit-1))) {
            return 1;
        }
        else {
            return 0;
        }
    },

    // simple profiling

	startTimer: function(label) {
		if (! this.timerStats) {
			this.timerStats = {};
		}
		if (! this.timerStats[label]) {
			this.timerStats[label] = {"starts" : [], "stops" : []};
		}
		this.timerStats[label].starts.push(new Date());		
	},

	stopTimer : function(label) {
		if (! this.timerStats 
			|| ! this.timerStats[label] 
			|| this.timerStats[label].stops.length >= this.timerStats[label].starts.length) {
			alert("prematurely called stopTimer. Make sure you call startTimer first for label " + label);
		}
		else {
			this.timerStats[label].stops.push(new Date());
		}
	},
	
	summarizeTimers : function() {
		var res = "";
		for (var key in this.timerStats) {
			var label_time = 0;
			var samples = this.timerStats[key].stops.length;
			for (var i = 0; i < samples; i++) {
				label_time += this.timerStats[key].stops[i].getTime() - this.timerStats[key].starts[i].getTime();		
			}
			res+="<br /> " + key + ": " + (label_time / samples) + "ms avg. -- " + samples + " sample(s) -- " + label_time + "ms total";
		}
		return res;
	},
	
	doOnDomLoad : function(func) {
		if (! this.m_dom_load_funcs) {
			this.m_dom_load_funcs = [];	
		}
		this.m_dom_load_funcs.push(func);
	},
	
	executeDomLoad : function() {
		if (this.m_dom_load_funcs) {
			for (var i = 0; i < this.m_dom_load_funcs.length; i++) {
				setTimeout(this.m_dom_load_funcs[i], 500);
			}
		}		
     },


	fillInTheP:function() {
		var temp=window.location.href;

		if (temp.indexOf("login?p=") != -1) {
			$('page_url_p').value = temp.substring(temp.indexOf("login?p=")+8);
		}
		else if (temp.indexOf("signup") != -1) {
			$('page_url_p').value ="/home";
		}
		else if (temp.indexOf("login") == -1 && temp.indexOf(".com") != -1) {
			temp=temp.substring(temp.indexOf(".com"));
			if (temp.indexOf("/") != -1) {
				temp=temp.substring(temp.indexOf("/"));
			}
			if (temp.length > 3) {
				$('page_url_p').value = temp;
			}
		}
	},
	
	hideBottomBar:function() {
		if ($('footer_signup_wrapper'))
			$('footer_signup_wrapper').hide();
	},
	
	toggleClass:function(b, c) {
		// get class the old school way because Prototype's hasClass() doesn't work well in IE
		var hasClass = b.attributes['class'].value.search(c);
		
		// using this method because add/removeClass() doesn't work well in IEs either
		if (hasClass != -1)
			b.className = b.className.replace(new RegExp(' '+c+'\\b'), '');
		else
			b.className += ' '+c;
	},
	// 
	constrainImageWidthAfterLoad : function(im, maxwidth) {
		if (im.width > maxwidth) {
			im.style.width = "";
			im.style.height = "";
			im.height = Math.round(maxwidth * im.height / im.width);
			im.width = maxwidth;
		}
	},

	// 

	floatADivOnDomLoad : function(id, parentid) {
		util.doOnDomLoad( function() {
			if ($(id)) document.observe('scroll', util.floatADivOnScrollHandler.bind(this,id,parentid));
			// this is for the IEs                                                                                   
			if (window.attachEvent && $(id)) window.attachEvent("onscroll", util.floatADivOnScrollHandler.bind(this,id, parentid));	
		});
	},
	
	// 
	floatADivOnScrollHandler : function(id, parentid) {
		var b = $(id);
		var par = $(parentid);
		var coff = par.cumulativeOffset()[1];
		var csoff = par.cumulativeScrollOffset()[1];
		if (coff < csoff) 
			b.style.position = "fixed";
		else 
			b.style.position = "static";
	},
	// 
	toMask : function() {
	  	var res = 0;
	
	  	if(typeof arguments[0] == "object")
			bits = arguments[0];
		else 
			bits = arguments;
			
	  	for (var i = 0; i < bits.length;i++) {
		 	if (bits[i] > 31 || bits[i] < 0)
				throw("fillBitsInMask expects ints in [0..30] due to JS bitwise limitations");
			res = res | Math.pow(2,bits[i]);
	  	}
	  	return res;
	},
	// 
	fromMaskToList : function(mask) {
		var res = [];
		for (var i = 0; i < 31; i++)
			if (Math.pow(2,i) & mask)
				res.push(i);
		return res;
	},
	// 
	isBitSetInMask : function(mask, bit) {
		return Math.pow(2, bit) & mask ? 1 : 0;
	}
	
	
};
/*  */

/* Further work and future integration */

function updateFavCount() {
	var num_favs_online = document.getElementById('favs_online').getElementsByTagName('li').length - 1;
	var favs_count_span = document.getElementById('favs_count');
	
	if (num_favs_online > 0) {
		favs_count_span.getElementsByTagName('strong')[0].innerHTML = num_favs_online;
		favs_count_span.style.display = "";
	}
}

function checkForm(tab){ // check if user has made a from entry

    if((FormId == "profileeditform") && (tab == "profileedit" ||  tab == "details")) {

        tab = (tab == "details" ? tab : "essays");

        $("tab-" + OldId).removeClassName("tab-on");
        $("tab-" + tab).addClassName("tab-on");
        displayDiv(tab);
    } else {
        if(FormId != 'picturerows' && SerializedForm != Form.serialize(FormId)) {

            $(FormId).action += "?tab=" + tab; // redirect to tabbed page after form submission

            if(FormId == "settingsform"){ // check if user entered password
    	        if(!document.getElementById("settingsSubmit").disabled) {
        		    $(FormId).submit(); // submit changes
        	    } else {
        		    alert("Please enter your password to save your settings.");
        		    document.getElementById("oldPassword").focus();
        	    }
            } else {
                $(FormId).submit(); // submit changes
            }

        } else { // send user to tabbed page

            if(tab == "details"){ // "details" and "essays" tab share the same page
                page = "profileedit";
            } else {
                page = tab;
            }
            document.location.href = "/" + page + "?tab=" + tab;
        }
    }
}

var util = Utilities;

// these functions probably have prototype equivalents; deprecate or move to utility array funcs
// Note: these do NOT have prototype equivalents; assess later, fine for now

/* standard?  how far back does this go? */

if(typeof Array.prototype.push=='undefined')
  Array.prototype.push=function(){
    var i=0;
    b=this.length;a=arguments;
    for(i;i<a.length;i++)this[b+i]=a[i];
    return this.length;
  };



function pushFront(A, x, maxsize) {
 var i=A.length;
 if (maxsize && i>maxsize - 1)
  i=maxsize - 1;
 while (i>0) {
  A[i] = A[i-1];
  i--;
 }
 A[0] = x;
}

function popMiddle(A,i) {
  var x;
  if (i>A.length-1)
    return (false);
  for (x=i; x<A.length-1; x++)
     A[x]=A[x+1];
  A.length=A.length-1;
  return (true);
}

function popFront(A) {
  return (popMiddle(A,0));
}

function moveToFront(A,i) {
 var temp=A[i];
 while (i>0) {
  A[i]=A[i-1];
  i--;
 }
 A[0]=temp;
}

// 

function $RF(el, radioGroup) {
    if($(el).type && $(el).type.toLowerCase() == 'radio') {
        var radioGroup = $(el).name;
        var el = $(el).form;
    } else if ($(el).tagName.toLowerCase() != 'form') {
        return false;
    }

    var checked = $(el).getInputs('radio', radioGroup).find(
        function(re) {return re.checked;}
    );
    return (checked) ? $F(checked) : null;
}


function favorites_toggle() {
    var toggle_text = $("favs_toggle");
    
    if($bit.get(33) == 1)
        toggle_text.innerHTML = "Show Offline";
    else
        toggle_text.innerHTML = "Hide Offline";
        
    util.toggle('favs_offline', 33);
}

function topics_toggle() {
    var topic_toggle = $("topic_toggle");
    
    if($bit.get(36) == 0)
        topic_toggle.innerHTML = "Show Topics";
    else
        topic_toggle.innerHTML = "Hide Topics";
        
	util.toggle('lb_recent_topics',36);
}





/* On DOM/window load events */
util.doOnDomLoad(
			function()
			{
				if($("ads3_set_leftbar")) Utilities.ads3_call("ads3_set_leftbar","showAd=1&adEvent=4&adEventAdId=1");
			
					// fill in the hidden p on logged out pages only
					if ($('header_login')) util.fillInTheP();
			
					// show the save profiles menu in the left bar for two ours (based on the cookie)
					if ($('lb_favorites_menu') && !NanoCookie.get('saved_display')) $('lb_favorites_menu').style.display = "";
					
					// adjust the page's height on load
					// you can move this if it makes you unhappy here [AS]
					Utilities.adjustMCHeight();
			}
);

if (window.attachEvent) window.attachEvent("onload", Utilities.windowsFixes);

Utilities.setDefaultText("mainMenuSearchBox", "search okcupid"); // old search box
Utilities.setDefaultText("site_search_query", "search okcupid"); // new search box

var GOOGLE_PUNCH = false;