Tuesday, October 11, 2011

Facebook Invites


// ==UserScript==
// @name           Facebook Invites
// @namespace      FacebookInvites
// @description    Invites your friends to the app if they've already allowed the app access.  Adds an bulk add form in message threads.
// @include        http://*.facebook.com/*
// @include        https://*.facebook.com/*
// @include        http://forums.zynga.com/*
// @require http://sizzlemctwizzle.com/updater.php?id=43682
// ==/UserScript==

/*

window.friendsWithAppList = List of friends with this app.
window.friendsWithAppNotInvited = List of friends with this app we can invite.
window.friendsWithAppId = The app id where we retreived the list of friends from.
window.friendsInInviteDialog = friends in the invite dialog

*/

//var SUC_script_num = 43682; // Change this to the number given to the script by userscripts.org (check the address bar)
//try{function updateCheck(forced){if ((forced) || (parseInt(GM_getValue('SUC_last_update', '0')) + (86400000*7) <= (new Date().getTime()))){try{GM_xmlhttpRequest({method: 'GET',url: 'http://userscripts.org/scripts/source/'+SUC_script_num+'.meta.js?'+new Date().getTime(),headers: {'Cache-Control': 'no-cache'},onload: function(resp){var local_version, remote_version, rt, script_name;rt=resp.responseText;GM_setValue('SUC_last_update', new Date().getTime()+'');remote_version=parseInt(/@uso:version\s*(.*?)\s*$/m.exec(rt)[1]);local_version=parseInt(GM_getValue('SUC_current_version', '-1'));if(local_version!=-1){script_name = (/@name\s*(.*?)\s*$/m.exec(rt))[1];GM_setValue('SUC_target_script_name', script_name);if (remote_version > local_version){GM_setValue('SUC_current_version', remote_version);if(confirm('There is an update available for the Greasemonkey script "'+script_name+'."\nWould you like to go to the install page now?')){GM_openInTab('http://userscripts.org/scripts/show/'+SUC_script_num);}}else if (forced)alert('No update is available for "'+script_name+'."');}else GM_setValue('SUC_current_version', remote_version+'');}});}catch (err){if (forced)alert('An error occurred while checking for updates:\n'+err);}}}GM_registerMenuCommand(GM_getValue('SUC_target_script_name', '???') + ' - Manual Update Check', function(){updateCheck(true);});updateCheck(false);}catch(err){}

// Checkbox list: http://apps.facebook.com/dragonwars/recruit.php
// Picture list: http://apps.facebook.com/egghunt/invite.php

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }

var nHtml={
FindByAttr:function(obj,tag,attr,className) {
 if(attr=="className") { attr="class"; }
 var q=document.evaluate(".//"+tag+"[@"+attr+"='"+className+"']",obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
 if(q && q.singleNodeValue) { return q.singleNodeValue; }
 return null;
},
FindByClassName:function(obj,tag,className) {
 return this.FindByAttr(obj,tag,"className",className);
},
FindByXPath:function(obj,xpath) {
 try {
  var q=document.evaluate(xpath,obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
 } catch(e) {
  GM_log('bad xpath:'+xpath);
 }
 if(q && q.singleNodeValue) { return q.singleNodeValue; }
 return null;
},
VisitUrl:function(url) {
 window.setTimeout(function() {
  document.location.href=url;
 },500+Math.floor(Math.random()*500));
},
ClickWin:function(win,obj,evtName) {
 var evt = win.document.createEvent("MouseEvents");
 evt.initMouseEvent(evtName, true, true, win,
  0, 0, 0, 0, 0, false, false, false, false, 0, null);
 return !obj.dispatchEvent(evt);
},
Click:function(obj) {
 return this.ClickWin(window,obj,'click');
},
ClickTimeout:function(obj,millisec) {
 window.setTimeout(function() {
  return nHtml.ClickWin(window,obj,'click');
 },millisec+Math.floor(Math.random()*500));
},
ClickUp:function(obj) {
 this.ClickWin(window,obj,'mousedown');
 this.ClickWin(window,obj,'mouseup');
 this.ClickWin(window,obj,'click');
},
GetText:function(obj,depth) {
 var txt='';
 if(depth==undefined) { depth=0; }
 if(depth>40) { return; }
 if(obj.textContent!=undefined) { return obj.textContent; }
 for(var o=0; o<obj.childNodes.length; o++) {
  var child=obj.childNodes[o];
  txt+=this.GetText(child,depth+1);
 }
 return txt;
}

};


function DebugLog(mess) {
 GM_log(mess);
}

FacebookInvites={
addAsFriendButtons:false,
addMeUpto:0,
addMeUptoMax:0,    // how many users do we try to add as friends on this page. Only 1 when we're on profile page.
addMeDone:{},
waitForAddMe:null,
waitForAddMeStarted:null,
bulkAddMeTimeout:null,
messObj:null,
allAppsId:"0ALLAPPS",
bulkAddOptions:{},
scriptName:'Facebook Invites',

SetMessage:function(mess) {
 if(!this.messObj) {
  var content=document.getElementById('content');
  if(!content) content=document.body;
  if(content) {
   var div=document.createElement('div');
   div.style.paddingTop='48px';
   div.innerHTML=mess;
   content.insertBefore(div,content.childNodes[0]);
   this.messObj=div;
  } else {
   GM_log('cannot find content div');
  }
 }
 this.messObj.innerHTML=mess;
},

IsTooManySelected:function() {
 var toomany=nHtml.FindByXPath(document,"//div[@class='toomany_selected']");
 if(toomany && toomany.style.display!='none') {
  GM_log('Too many friends selected');
  return true; 
 }
 return false;
},

InviteFriendsCheckBox:function(div,friendsHash,click) {
 var inps=div.getElementsByTagName('input');
 var friendsClicked=0;
 var selected={};
 var isSmallPictures=this.IsFriendsSmallPictures(div);

 for(var i=0; i<inps.length; i++) {
  var inp=inps[i];
  var val=""+inp.value;
  if(friendsHash!=null && !friendsHash[val]) {
   continue;
  }
  if(selected[val]) { continue; }
  var sel=true;
  if(isSmallPictures) {
   if(inp.parentNode.className.indexOf("selectedCheckable")>=0) {
    sel=true;
   } else { sel=false; }
  } else {
   if(inp.id!="ids[]") {
    continue;
   }
   if(inp.parentNode.parentNode.className=="unselected_list") {
    sel=false;
   }
  }
  if(inp.parentNode.style.display=='none') { continue; }
  if(click(sel,val)) {
   selected[val]=1;
   nHtml.Click(inp.parentNode);
   friendsClicked++;
   if(this.IsTooManySelected()) {
    break;
   }
  }
 }
 return friendsClicked;
},
InviteFriendsPictures:function(div,friendsHash,click) {
 var lis=div.getElementsByTagName('li');
 var selected=0;
 for(var l=0; l<lis.length; l++) {
  var li=lis[l];
  var as=li.getElementsByTagName('a');
  if(as.length<1) { continue; }
  var userid=""+li.getAttributeNode('userid').value;
  if(friendsHash!=null && !friendsHash[userid]) {
   continue;
  }
  if(click(li.className=='selected',userid)) {
   nHtml.Click(as[0]);
   selected++;
   if(this.IsTooManySelected()) {
    break;
   }
  }
 }
 return selected;
},
InviteFriendsClick:function(div,friendsHash,click) {
 if(!this.IsFriendsDivPictures(div) || this.IsFriendsSmallPictures(div)) {
  return this.InviteFriendsCheckBox(div,friendsHash,click);
 }
 return this.InviteFriendsPictures(div,friendsHash,click);
},

IsFriendsDivPictures:function(div) {
 if(div.className.indexOf("condensed_multi_friend_selector")>=0) {
  return false;
 }
 return true;
},

IsFriendsSmallPictures:function(div) {
 if(div.className.indexOf("fbProfileBrowserListContainer")>=0) {
  return true;
 }
 return false;
},
GetFriendsDiv:function() {
 var friends2=nHtml.FindByClassName(document.body,"div","condensed_multi_friend_selector");
 if(friends2) { return friends2; }
 friends2=nHtml.FindByClassName(document.body,"div","fbProfileBrowserListContainer");
 if(friends2) { return friends2; }
 var friends=document.getElementById("friends");
 return friends;
},

DoIFrame:function(url) {
 var f=document.getElementById('FBInvitesFrame');
 if(!f) {
  f=document.createElement('iframe');
  f.style.display='none';
  f.id='FBInvitesFrame';
  document.body.appendChild(f);
 }
 f.src=url;
 return f;
},

previousStr:'this',
InviteFriends:function() {
 if(document.getElementById('InviteFriendRefresh')) {
  // we've already done this page.
  return;
 }

 
 var friends=this.GetFriendsDiv();
 if(!friends) {
  return false;
 }

 var link=document.createElement('a');
 link.href='javascript:;';
 link.id='InviteFriendRefresh';
 this.previousStr='this';

 var appId=this.GetAppId();
 var oldAppId=GM_getValue('appid','');
 var linkMess=document.createElement('span');
 linkMess.innerHTML='';

 link.innerHTML="&bull; Refresh your list of friends who are using this application.";
 link.title="Go to the 'friends who are using this applications' page. This needs to be done before new friends will be selected here.";
 if(!appId && !oldAppId) {
  linkMess.innerHTML="Need to visit an application's invite page to collect a list of friends first!";
 } else if(!appId) {
  // we're in the facebook friends list or some other non-app thing.
  link.innerHTML="";
  this.previousStr='previous';
 }

 var friendsHash=this.GetFriendsWithApp();
 var appFriendsHash=null;
 if(appId && oldAppId) {
  var lastSync=GM_getValue('FriendsWithAppSyncTime_'+appId,'');
  if(lastSync!='') {
   appFriendsHash=this.GetFriendsWithApp(appId);
   friendsHash=appFriendsHash;
   var lastSyncD=new Date();
   lastSyncD.setTime(lastSync);
   var alertStyle='';
   var now=new Date().getTime();
   if(lastSync<(now-86400*1000*3))  {
    alertStyle="style='color: #f00'";
   }
   linkMess.innerHTML+=" <b "+alertStyle+">Friends list last refreshed on: "+lastSyncD.toString()+"</b><br />";
  } else if(appId!=oldAppId) {
   linkMess.innerHTML+=" <b>Warning! The list of friends are from another app.</b><br />";
   this.previousStr='previous';
  }
 }

 if(appFriendsHash==null && appId) {
  // don't have anything in the memory for this app, first time user...
//  this.VisitFriendsList();
//  link.innerHTML="Important: "+link.innerHTML;
  link.style.fontSize='15pt';
 }
 

 var css=document.createElement('style');
 css.type='text/css';
 css.innerHTML='#InviteFriendsDiv a { color: #008;  } #InviteFriendsDiv a:hover { text-decoration: underline; }';
 document.getElementsByTagName('head')[0].appendChild(css);

 var managementDiv=document.createElement('div');
 managementDiv.innerHTML="You can also grab the users from any page via the greasemonkey icon under 'user script commands'<br />";
 if(appId) {
  // we're in an app, not facebook. let's hide the friends management section.
  managementDiv.style.display='none';
 }
 managementDiv.style.marginLeft='10px';
 var managementLink=document.createElement('a');
 managementLink.innerHTML='&bull; Friends management';
 managementLink.title='Open up the friends management area.  More functions to handle friends ';
 managementLink.addEventListener('click',function() {
  managementDiv.style.display=managementDiv.style.display=='none'?'block':'none';
 },false);

 var linkDiv=document.createElement('div');
 this.messObj=document.createElement("div");
 this.messObj.style.fontSize='14pt';
 linkDiv.id='InviteFriendsDiv';
 linkDiv.appendChild(this.messObj);
 linkDiv.appendChild(linkMess);
 linkDiv.appendChild(link);
 linkDiv.appendChild(document.createElement("br"));
 linkDiv.appendChild(managementLink);
 linkDiv.appendChild(document.createElement("br"));
 linkDiv.appendChild(managementDiv);
 linkDiv.style.border='2px solid #888';
 linkDiv.style.padding='4px';
 linkDiv.style.color='#444';
 linkDiv.style.backgroundColor='#fff'; 

 var clearLink=document.createElement('a');
 clearLink.href='javascript:;';
 clearLink.id='InviteFriendClear';
 clearLink.innerHTML='Deselect';

 var selectAllLink=document.createElement('a');
 selectAllLink.innerHTML='Select';

 var bull=document.createElement("font")
 bull.innerHTML="&bull; ";
 linkDiv.appendChild(bull);
 linkDiv.appendChild(selectAllLink);
 linkDiv.appendChild(document.createTextNode(" / "));
 linkDiv.appendChild(clearLink);
 linkDiv.appendChild(document.createTextNode(" all friends."));
 linkDiv.appendChild(document.createElement("br"));

 var removeLink=document.createElement('a');
 removeLink.innerHTML='Deselect';
 var unionLink=document.createElement('a');
 unionLink.innerHTML='Join';
 unionLink.title='Only select friends using '+this.previousStr+' application and friends currently selected here.';
 
 var appUidslink=document.createElement('a');
 appUidslink.onclick='javascript:return false;';
 appUidslink.innerHTML='Friends in game';
 appUidslink.target='_blank';
 var appNotInvitedUidslink=document.createElement('a');
 appNotInvitedUidslink.onclick='javascript:return false;';
 appNotInvitedUidslink.target='_blank';
 appNotInvitedUidslink.innerHTML='In game and in invite box';

 var addLink=document.createElement('a');
 addLink.innerHTML='Select';
 var bull=document.createElement("font");
 bull.innerHTML="&bull; ";
 var bull2=document.createElement("font");
 bull2.innerHTML="&bull; ";
 
 managementDiv.appendChild(bull2);
 managementDiv.appendChild(document.createTextNode("Copy link to copy uids:"));
 managementDiv.appendChild(appUidslink);
 managementDiv.appendChild(document.createTextNode(" / "));
 managementDiv.appendChild(appNotInvitedUidslink);
 managementDiv.appendChild(document.createElement("br"));
 
 managementDiv.appendChild(bull);
 managementDiv.appendChild(addLink);
 managementDiv.appendChild(document.createTextNode(" / "));
 managementDiv.appendChild(removeLink);
 managementDiv.appendChild(document.createTextNode(" / "));
 managementDiv.appendChild(unionLink);
 managementDiv.appendChild(document.createTextNode(' Friends from the current list.'));

 var copyLink=document.createElement('a');
 copyLink.innerHTML='&bull; Use the selected friends below as the current list.';
 managementDiv.appendChild(document.createElement("br"));
 managementDiv.appendChild(copyLink);

 var useBufferLink=null;
 if(appFriendsHash!=null) {
  useBufferLink=document.createElement('a');
  useBufferLink.innerHTML="&bull; Use the previous application's friends list for select/deselect/join.";
  managementDiv.appendChild(document.createElement("br"));
  managementDiv.appendChild(useBufferLink);
 }

 joinRecentLink=document.createElement('a');
 joinRecentLink.innerHTML="&bull; Only include friends who are also in the first page of 'recently added friends'.";
 linkDiv.appendChild(joinRecentLink);
 linkDiv.appendChild(document.createElement("br"));

 var randomLink=document.createElement('a');
 randomLink.href='javascript:;';
 randomLink.innerHTML='&bull; Add random friends who are using '+this.previousStr+' application.';
 linkDiv.appendChild(randomLink);

 var detachLink=document.createElement('a');
 detachLink.innerHTML='&bull; Move this block to the top';
 detachLink.title="If you're having visibility problems";
 linkDiv.appendChild(document.createElement("br"));
 linkDiv.appendChild(detachLink);


 var contentDiv=document.getElementById('content');
 if(!contentDiv) contentDiv=document.body;
 var randomDialog=document.createElement('div');
 randomDialog.innerHTML="<br /><br /><form><input type='checkbox' id='AddRandomFriendsAnyFriends' />Add any friends, not just people who're using the app.<br />Friends to add: <input id='AddRandomFriendsNum' size='4' value='1' /><input id='AddRandomFriendsButton' type='button' value='Add Random Friends' /></form>";
 randomDialog.style.border="2px solid #888";
 randomDialog.style.padding='4px';
 randomDialog.style.position='fixed';
 randomDialog.style.backgroundColor='#fff';
 randomDialog.style.color='#000';
 randomDialog.style.left=0;
 randomDialog.style.top=0;
 randomDialog.style.display="none";
 contentDiv.appendChild(randomDialog);

 var randomButton=document.getElementById('AddRandomFriendsButton');

 var inviteToggle=this.GetDivToggle(linkDiv,'ShowInviteFriends',true);
 inviteToggle.innerHTML='<b>'+this.scriptName+': Show invite friends block</b>';
 inviteToggle.style.backgroundColor='#fff';
 inviteToggle.style.color='#008';
 inviteToggle.style.padding='4px';
 
 
// if(!this.IsFriendsDivPictures(friends)) {
//  friends.parentNode.insertBefore(linkDiv,friends);
// } else {
  friends.parentNode.insertBefore(inviteToggle,friends);
  friends.parentNode.insertBefore(linkDiv,friends);
// }
 link.addEventListener('click',function() { FacebookInvites.VisitFriendsList(); },false);

 copyLink.addEventListener('click',function() { 
  var friendsList=[];
  friendsHash={};
  FacebookInvites.InviteFriendsClick(friends,null,function(c,id) { 
   if(c) {
    friendsHash[id]=1; 
    friendsList.push(id); 
   }
   return false; 
  });
  var friendsListStr=friendsList.join(',');
  GM_setValue('FriendsWithApp',friendsListStr);
  FacebookInvites.SetMessage("The list we're using now has "+friendsList.length+" friends.");
 },false);
 selectAllLink.addEventListener('click',function() { 
  FacebookInvites.InviteFriendsClick(friends,null,function(c) { return c?false:true; });
 },false);
 clearLink.addEventListener('click',function() { 
  FacebookInvites.InviteFriendsClick(friends,null,function(c) { return c?true:false; });
 },false);
 removeLink.addEventListener('click',function() { 
  FacebookInvites.InviteFriendsClick(friends,friendsHash,function(c) { return c?true:false; });
 },false);
 unionLink.addEventListener('click',function() { 
  FacebookInvites.InviteFriendsClick(friends,null,function(c,id) { 
   if(c) return friendsHash[id]?false:true;
   return false;
  });
 },false);

 if(useBufferLink!=null) {
  useBufferLink.addEventListener('click',function() { 
   friendsHash=FacebookInvites.GetFriendsWithApp();
   FacebookInvites.CalcInvitedHash(friendsHash);
  },false);
 }

 joinRecentLink.addEventListener('click',function() { 
  // visit recent added friends in iframe, 
  var iframe=FacebookInvites.DoIFrame('http://www.facebook.com/friends/?added&ref=tn');

  GM_log('Waiting for recent friends to load');
  unsafeWindow.joinRecentLoadedLink=function() { 
   GM_log('Recent friends loaded');
   var recentFriends=FacebookInvites.GetFriendsFromPage(iframe.contentDocument.body);
   var ids=[];
   for(var id in friendsHash) { ids.push(id); }
   var t=0;
   ids.forEach(function(id) {
    if(!recentFriends[id]) {
     friendsHash[id]=undefined;
    } else {
     t++;
    }
   });
   FacebookInvites.InviteFriendsClick(friends,null,function(c) {
    if(c) {
     return true; 
    }
   });
   FacebookInvites.InviteFriendsClick(friends,null,function(c,id) {
    var r=friendsHash[id]?true:false;
    if(c) { r=r?false:true; GM_log('huh? something is still selected?'); }
    return r;
   });
   FacebookInvites.SetMessage("The list we're using now has "+t+" friends.");
   iframe.src='about:blank';
   document.body.removeChild(joinRecentLoadedLink);
  };
 },false);
 addLink.addEventListener('click',function() { 
  FacebookInvites.InviteFriendsClick(friends,friendsHash,function(c) { return c?false:true; });
 },false);
 randomLink.addEventListener('click',function() {
  randomDialog.style.display='block';
 },false);
 detachLink.addEventListener('click',function() {
  FacebookInvites.DetachDiv();
 },false);


 randomButton.addEventListener('click',function() {
  randomDialog.style.display='none';
  var randomNum=document.getElementById('AddRandomFriendsNum');
  var wanted=parseInt(randomNum.value);
  var friendsHashRandom={};
  var added=0;

  var anyFriends=document.getElementById('AddRandomFriendsAnyFriends').checked;
  var friendsInDialog={}; // friends that are not selected in the dialog
  FacebookInvites.InviteFriendsClick(friends,null,function(c,id) {  if(!c) { friendsInDialog[id]=1; } return false; } );

  var friendsArr=[];
  var hashToUse=friendsHash;
  if(anyFriends) {
   hashToUse=friendsInDialog;
  }
  for(var f in hashToUse) {
   friendsArr.push([f,Math.random()]);
  }

  friendsArr.sort(function(a,b) { return a[1]-b[1]; });

  for(var fUpto=0; fUpto<friendsArr.length; fUpto++)
  {
   if(added>=wanted) {
    break;
   }
   var f=friendsArr[fUpto][0];
   if(!friendsInDialog[f]) { continue; }
   friendsHashRandom[f]=""+friendsHash[f];
   added++;
  }
  FacebookInvites.InviteFriendsClick(friends,friendsHashRandom,function(c,id) { return c?false:true; });
 },false);

 window.friendsWithAppId=oldAppId;
 this.CalcInvitedHash(friendsHash);
 this.InviteFriendsClick(friends,friendsHash,function(c,id) { 
  var r=c?false:true;

/* we don't have an appid anymore in iframed invite dialogs.
  if(!appId) {
   // can't find app id, we're in facebook, lets' not auto add all the friends.
   return false;
  }
*/  
  return r;
 });

 appUidslink.href='about:blank#'+this.MakeCsvFromHashNames(friendsHash);
 appNotInvitedUidslink.href='about:blank#'+this.MakeCsvFromHashNames(window.friendsWithAppNotInvited);
 
 return true;
},

MakeCsvFromHashNames:function(friendsHash) {
 var uidsStr='';
 for(var n in friendsHash) {
  if(uidsStr.length>0) uidsStr+=",";
  uidsStr+=n;
 }
 return uidsStr;
},

GetInviteFriendsDiv:function() {
 return document.getElementById('InviteFriendsDiv');
},
DetachDiv:function() {
 var linkDiv=this.GetInviteFriendsDiv();
 if(!linkDiv) { return; }
 linkDiv.style.position='fixed';
 linkDiv.style.left=0;
 linkDiv.style.top='24px';
},
CalcInvitedHash:function(friendsHash) {
 var friendsDiv=this.GetFriendsDiv();
 var totalFriendsWithApp=0;
 for(var f in friendsHash) {
  totalFriendsWithApp++;
 }
 var invited=0;
 var invitedHash={};
 var friendsInInviteDialog={};
 this.InviteFriendsClick(friendsDiv,null,function(c,id) { 
  if(friendsHash[id]) {
   var r=c?false:true;
   if(r) invitedHash[id]=1;
   if(r) { invited++; }
  }
  friendsInInviteDialog[id]=1;
  return false;
 });
 window.friendsWithAppList=friendsHash;
 window.friendsWithAppNotInvited=invitedHash;
 window.friendsInInviteDialog=friendsInInviteDialog;
 this.SetMessage(" (Friends with "+this.previousStr+"  application that you can invite:"+invited+" out of "+totalFriendsWithApp+")");
},


idRe:new RegExp('[^a-z]id=([0-9]+)','i'),
GetAppId:function() {
 var builtBy=nHtml.FindByClassName(document.body,"span","page_built_by");
 if(builtBy) {
  var as=builtBy.getElementsByTagName("a");
  if(as.length>0) {
   var m=this.idRe.exec(as[0].href);
   if(m && m.length>0) {
    var id=m[1];
    return id;
   }
  }
 }

 var appContentDiv=nHtml.FindByXPath(document.body,"//div[contains(@id,'app_content_')]");
 if(appContentDiv) {
  var m=/^app_content_([0-9]+)$/.exec(appContentDiv.id);
  if(m && m.length>1) {
   return m[1];
  }
 }
 var api_key_input=nHtml.FindByXPath(document.body,"//input[@name='api_key' and @type='hidden']");

 // the new small pictures invite screen.
 if(api_key_input) return api_key_input.value;

 return null;
},

VisitFriendsList:function() {
 var appid=this.GetAppId();
 GM_setValue('appid',appid);
 GM_setValue('FriendsWithApp','');
 GM_setValue('InviteUrl',document.location.href);

// document.location.href="http://www.facebook.com/social_graph.php?node_id="+appid+"&class=AppUserManager&edge_type=mutual&start=0"; //change suggested by Lox Myth - see http://userscripts.org
http://www.facebook.com/browse/?type=friends_using_app&app_id=268652830776/topics/34630#posts-166598

 var url="http://www.facebook.com/browse/?type=friends_using_app&app_id="+appid;
 GM_log('Going to get friends with apps list:'+url);
 location.href=url;
 return true;
},

GetFriendsWithApp:function(appId) {
 var friends=GM_getValue('FriendsWithApp'+(appId!=undefined?("_"+appId):''),'').split(/,/);
 var friendsHash={};
 for(var f=0; f<friends.length; f++) {
  var n=friends[f];
  if(n=="") { continue; }
  friendsHash[n]=1;
 }
 return friendsHash;
},

AddFriendsWithApp:function() {
 var inviteUrl=GM_getValue('InviteUrl','');
 if(inviteUrl=='') { return; }
 var content=document.getElementById('content');
 if(!content) { return; }
 var as=content.getElementsByTagName('a');
 var nextUrl=null;
 var friends=this.GetFriendsWithApp();

 this.ClickClose();

 // var ss=document.evaluate("//a[contains(@href,'/social_graph.php') and contains(@title,'Next')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null); //change suggested by Lox Myth - see http://userscripts.org/topics/34630#posts-166598
  //it doesn't work with different localization
  // suggested by: jcppkkk
// var ss=document.evaluate("//span[@class!='UIPager_ButtonWrapper UIPager_ButtonDisabled']/a[@class='UIPager_Button UIPager_ButtonForward']",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

 var ss=document.evaluate("//a[contains(@class,'uiMorePagerPrimary') and contains(@href,'friends_using_app')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

 if(ss.snapshotLength>0) {
  var lastpager=ss.snapshotItem(ss.snapshotLength-1);
//  var numberRe=/^[\\s0-9]+$/;
//  if(!numberRe.exec(lastpager.innerHTML)) {
   // we still have a "next" button.
   GM_log('Next page found:'+lastpager.href);
   nextUrl=lastpager.href;
//  }
 } else {
  GM_log('No next page found');
 }



 var friendsAdded=0;
 var friendIds=this.GetFriendsFromPage(content);
 for(var id in friendIds) {
  if(friends[id]) { continue; }
  friends[""+id]=1;
  friendsAdded++;
 }

/*
 for(var aUpto=0; aUpto<as.length; aUpto++) {
  var a=as[aUpto];
//  if(a.href.indexOf("/friends/")<0) { //change suggested by Austin Pereira - http://userscripts.org/topics/34741#posts-167127


  var id=this.GetFriendIdFromLink(a,this.profileRe);
if(false) {
  if(a.parentNode.className.indexOf('fsl fwb fcb')<0) {
   continue;
  }
  var m=this.idRe.exec(a.href);
  if(m && m.length>0) {
   id=m[1];
  } else { continue; }
} 

  if(friends[id]) { continue; }
  var name=a.innerHTML;
  friends[""+id]=1;
  friendsAdded++;
 }

*/
 var friendsListStr=this.SaveFriendsFromHash(friends);

 var appid=GM_getValue('appid',0);
 GM_setValue('FriendsWithApp_'+appid,friendsListStr);
 GM_setValue('FriendsWithAppSyncTime_'+appid,(new Date()).getTime().toString() );

 if(nextUrl!=null) {
  nHtml.VisitUrl(nextUrl);
 } else {
  // cannot find "next" button, lets go back to app.
  GM_setValue('InviteUrl','');
  GM_log('Going back to previous url in app:'+inviteUrl);
  nHtml.VisitUrl(inviteUrl);
 }
 return friendsAdded>0?true:false;
},
SaveFriendsFromHash:function(friends) {
 var friendsList=[];
 for(var n in friends) {
  if(n=="") {continue; }
  friendsList.push(n);
 }
 var friendsListStr=friendsList.join(',');
 GM_setValue('FriendsWithApp',friendsListStr);
 return friendsListStr;
},


StartBulkAddMe:function() {
 this.addMeUpto=0;
 this.RestartAddMe();
},
SetupBulkAddMeProfileButton:function() {
 var button=document.getElementById('BulkAddMeProfileButton');
 var autoBulkAdd=this.GetAutoBulkAdd();
 
 button.style.color=autoBulkAdd?'#c00':'';
 button.value=(autoBulkAdd?"Disable ":"Enable")+" auto add on profile Page";
},

AddMeLink:function(lp) {
 if(document.getElementById("AddMeLinkDiv")) { return; }
 if(!lp) lp=document.getElementById("all_threads");
// if(!lp) {   lp=document.getElementById("see_all_posts"); }
 if(!lp) {
  return false;
 }
 var div=document.createElement('div');
 div.id='AddMeLinkDiv';

 var bulkAddToggle=this.GetDivToggle(div,'ShowBulkAdd',true);
 bulkAddToggle.innerHTML='<b>'+this.scriptName+': Show bulk add friends block</b>';

 var autoBulkAdd=FacebookInvites.GetAutoBulkAdd();

 div.innerHTML=
  (this.GetAutoBulkAdd() && window.name!=""?'<a href="javascript:window.parent.CloseIFrameByName(window.name)">Close frame</a><br />':'')+
  "<form id='BulkAddMe'>Comment:<textarea id='bulkAddComment' name='comment' cols='40' rows='4' /></textarea><br />"+
  "Friend List to add confirmed friends to:<input id='bulkAddFriendList' />(exactly with capital letters)<br />"+
  "<input type='checkbox' id='BulkAddMeButtonPages' />Bulk add everyone here and the previous pages of this discussion board<br />"+
  "<input id='bulkAddDelay'  value='10' size='4' />seconds between adds<br />"+
  "<input type='checkbox' id='popupToAddFriend' checked title='Popup profile to add friend' />Add user via the users' profile instead. (This makes sure that we can add this user first before trying to add.)<br />"+
  "<a id='BulkAddOptionsToggle' href='javascript:;'>Options</a><br />"+
  "<input type='button' id='BulkAddMeButton' value='Bulk Add Everyone here' /><br />"+
  "<input type='button' id='BulkAddMeProfileButton' value=''  title='Useful for pages that do not have the add friend feature or is outside of facebook.' />(Once enabled, you can click with the middle mouse button on any profile link to add them)<br />"+
  "</form>";

  
 div.style.border='1px solid #000';
 div.style.padding='10px';
 lp.parentNode.insertBefore(bulkAddToggle,lp);
 lp.parentNode.insertBefore(div,lp);
 
 var divOptions=document.createElement('div');
 divOptions.id='AddMeOptionsDiv';
 divOptions.innerHTML= 
  "<input type='checkbox' id='BulkAddNewsFeed' />Publish to news feed<br />"+
  "<input type='checkbox' id='testBulkAddMeButton' title='Test Only, do not add, just go through the process.' />Test only<br />"+
  "<input type='checkbox' id='BulkAddIgnoreErrors' />Ignore errors, continue even when facebook returns an errors<br />";
 divOptions.style.display='none';



 var toggle=document.getElementById('BulkAddOptionsToggle');
 toggle.parentNode.insertBefore(divOptions,toggle.nextSibling);
 this.SetupBulkAddMeProfileButton();

 toggle.addEventListener('click',function() {
  divOptions.style.display=divOptions.style.display=='none'?'block':'none';
 },false);

 var t=this;
 var bulkAddMeButton=document.getElementById('BulkAddMeButton');
 bulkAddMeButton.addEventListener('click',function() {
  var addAsFriend=t.FindNthAddAsFriends();
  if(addAsFriend) {
   document.getElementById('popupToAddFriend').checked=false;
   t.addAsFriendButtons=addAsFriend.snapshotLength;
  } else { t.addAsFriendButtons=0; }
  t.StartBulkAddMe();
 },false);

 var bulkAddMeProfile=document.getElementById('BulkAddMeProfileButton');
 bulkAddMeProfile.addEventListener('click',function() {
  GM_setValue('autoBulkAdd_RemoveWhenFinished',false);
  //this.autoBulkAdd_RemoveWhenFinished=false;
  t.SetAutoBulkAdd(t.GetAutoBulkAdd()?false:true);
  t.SetupBulkAddMeProfileButton();
 },false);

 return true;
},

AddMeLinkAnyPage:function() {
 if(document.getElementById("AddMeLinkDiv")) { return; }
 var content=document.getElementById('content');
 if(!content) content=document.body;
 var p=document.createElement('p');
 p.innerHTML="<br /><br />";
 content.insertBefore(p,content.childNodes[0]);
 return FacebookInvites.AddMeLink(content.childNodes[1]);
},

AddAsFriendLinks:function() {
 var pop_content=document.getElementById('pop_content');
 if(!pop_content) return;
 var list=nHtml.FindByXPath(pop_content,".//div[contains(@id,'object_browser_content_area')]");
 if(!list) return;
 
 this.AddMeLinkAnyPage();
},


RestartAddMe:function() {
 if(this.bulkAddMeTimeout!=null) {
  window.clearTimeout(this.bulkAddMeTimeout);
  this.bulkAddMeTimeout=null;
 }
 this.SetWaitForAddMe(null);
 this.addMeDone={};
 var o={};
 o.friendList=document.getElementById('bulkAddFriendList').value.trim();
 o.comment=document.getElementById('bulkAddComment').value;
 o.delay=document.getElementById('bulkAddDelay').value;
 o.testAddMe=document.getElementById('testBulkAddMeButton').checked;
 o.ignoreErrors=document.getElementById('BulkAddIgnoreErrors').checked;
 o.doPrevPages=document.getElementById('BulkAddMeButtonPages').checked;
 o.newsFeed=document.getElementById('BulkAddNewsFeed').checked;
 if(this.IsPopupToAddFriend()) {
  //this.autoBulkAdd_RemoveWhenFinished=true;
  GM_setValue('autoBulkAdd_RemoveWhenFinished',true);
  FacebookInvites.SetAutoBulkAdd(true);
 }
 this.bulkAddOptions=o;
 
 this.BulkAddMe(new Date().getTime());
},

IsPopupToAddFriend:function() {
 var p=document.getElementById('popupToAddFriend');
 if(p && p.checked) { return true; }
 return false;
},

HasAddFriendDialog:function(a) {
 var addFriendDialog=false;
 if(a.href && a.href.indexOf('profile')>=0) {
  addFriendDialog=true;
 }
/*
 var oncl=a.getAttribute('onclick');
 if(oncl && oncl.indexOf('show_addfriend_dialog')>=0) {
  addFriendDialog=true;
 }
*/
 return addFriendDialog;
},

FindNthAddAsFriends:function(nth) {
 var popDialogss=document.evaluate(".//div[contains(@id,'pop_content')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 for(var s=0; s<popDialogss.snapshotLength; s++) {
  var pop_content=popDialogss.snapshotItem(s);

  var ss=document.evaluate("//a[contains(@class,'UIActionButton')]",pop_content,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
  return ss;
 }
 return null;
},
FindNthAddAsFriend:function(nth) {
 var ss=this.FindNthAddAsFriends();
 
 if(ss && nth<ss.snapshotLength) {
  return ss.snapshotItem(nth);
 }

 return null;
},

FindNthPost:function(nth) {
 var content=document.getElementById('content');
 if(location.href.indexOf('.facebook.com')<0) {
  content=document.body;
 }
 if(!content) return;
 var as=content.getElementsByTagName('a');
 var upto=0;
 for(var aUpto=0; aUpto<as.length; aUpto++) {
  var a=as[aUpto];
  var addFriendDialog=this.HasAddFriendDialog(a);

  if(!addFriendDialog && a.href.indexOf('k=')<0) { 
   // this person is already our friend.
   continue; 
  }

  // author_post: used in discussions
  // profile_link: used in reviews
  if(!addFriendDialog && a.className!="author_post" && a.className!="profile_link") {
   continue; 
  }
  if(a.parentNode.className.indexOf("fb_menu")>=0 || a.innerHTML.indexOf('<img')>=0) {
   continue;
  }
  var pn=a;
  var presence=false;
  while(pn && pn.tagName!="BODY") {
   if(pn.id=="presence") {
    presence=true;
   }
   pn=pn.parentNode;
  }
  if(presence) { continue; }
  if(nth==upto++) { return a; }
 }
 return null;
},


ClickFriendListDropDown:function(div) {
 GM_log('ClickFriendListDropDown');
 //var friendAdd=nHtml.FindByAttr(div,'span','class','FriendAddingTool_Menu');
 var friendAdd=nHtml.FindByXPath(div,".//a[contains(@class,'uiSelectorButton')]");
 if(friendAdd) {
   nHtml.ClickUp(friendAdd);
 /*
  var friendAddDropDown=nHtml.FindByAttr(friendAdd,'span','class','UIActionMenu_Text');

  // show the drop down for friends' lists
  if(friendAddDropDown) {
   nHtml.ClickUp(friendAddDropDown);
  } else {
   GM_log('cannot find friend list drop down');
  }
  */
 } else {
  GM_log('cannot find the friends list drop down');
 }
},
GetFriendListDropDown:function(div) {
 if(!div) { GM_log('GetFriendListDropDown: no div'); }
 //return nHtml.FindByAttr(div,'div','class','FriendAddingTool_InnerMenu');
 var dropDown=nHtml.FindByXPath(div,".//a[contains(@class,'uiSelectorButton') and contains(@class,'selected')]");
 if(dropDown) {
  dropDown=dropDown.parentNode;
 }
 return dropDown;
},
SelectFriendListDropDown:function(friendAddSelect,friendList) {
 GM_log('SelectFriendListDropDown');
 if(!friendAddSelect) { GM_log('Bad!, no friendAddSelect'); }
 var friendListItem=nHtml.FindByXPath(friendAddSelect,".//li[string()='"+friendList+"']");
 if(friendListItem) {
   nHtml.ClickUp(friendListItem);
   GM_log('click friend list item:'+friendListItem.innerHTML);
 } else {
  GM_log('Cannot find friend List item:'+friendList);
 }
},

SkipButton:function() {
 // skip reported suggest a friend dialogs from facebook.
 var skip=document.getElementById('skip');
 if(skip && skip.tagName=="INPUT") { 
  GM_log('Clicking skip button');
  nHtml.Click(skip);
  return true;
 }
 return false;
},

SetWaitForAddMe:function(v) {
 this.waitForAddMe=v;
 this.waitForAddMeStarted=(new Date()).getTime(); 
},

CloseIFrameOnCompletion:function() {
 if(window.name=="" || !unsafeWindow.parent) return;
 var t=this;
 try {
  window.setTimeout(function() {
   t.FindCaptcha();
   if(!t.hasCaptcha) {
    if(unsafeWindow.parent && unsafeWindow.parent.CloseIFrameByName) {
     GM_log('closing iframe:'+window.name);
     unsafeWindow.parent.CloseIFrameByName(window.name);
    }
   } else {
    GM_log('Has captcha do not close iframe:'+window.location.href);
   }
   t.CloseIFrameOnCompletion();
  },5000);
 } catch(e) {
  GM_log('close iframe problem:'+e);
 }
},

FindCaptcha:function() {
 var captcha=document.getElementById('captcha_session');
 this.hasCaptcha=captcha?true:false;
 return captcha;
},

hasCaptcha:false,
BulkAddMe:function(startTime) {
 var waitMillis=250;
 var error=document.getElementById('error');
 if(error && !this.bulkAddOptions.ignoreErrors) {
  var visible=true;
  while(error.tagName!="BODY") {
   if(error.style.display=="none") {
    visible=false;
    break;
   }
   error=error.parentNode;
  }
  if(visible) {
   GM_log('Error found, bulk add aborted');
   //if(GM_getValue('autoBulkAdd_RemoveWhenFinished',false))
   if(this.autoBulkAdd_RemoveWhenFinished && !this.profilePageAdd)
    this.SetAutoBulkAdd(false);
   return;
  }
 }

 this.ClickClose();

// DebugLog(this.waitForAddMe+","+this.addMeUpto);
 var captcha=this.FindCaptcha();
 var cp=captcha;
 while(cp && cp.tagName!="BODY") {
  if(cp.tagName=="FORM") {
   if(cp.action && cp.action.indexOf('edittopic')>=0) {
    // ignore captcha for making forum posts.
    captcha=undefined;
   }
   break;
  }
  cp=cp.parentNode;
 }

 var popDialogss=document.evaluate(".//div[contains(@class,'pop_dialog')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 //var popDialog=document.getElementById("pop_content");
 var popDialog=null;
 for(var popss=0; popss<popDialogss.snapshotLength; popss++) {
  var pd=popDialogss.snapshotItem(popss);
  var inps=pd.getElementsByTagName('INPUT');
  if(!inps || inps.length==0) { continue; }
//  var friendAddingTool=nHtml.FindByXPath(pd,".//div[contains(@class,'FriendAddingTool')]");
//  if(!friendAddingTool) continue;
  popDialog=pd;
  break;
 }

 if(this.SkipButton()) {
  // LoveYou43v3r reported that it got stuck in some skip dialogs.
  waitMillis=10000;
 } else if(captcha && popDialog) {
  // yuck captcha, wait for the user to do something.
  GM_log('found captcha, wait for user input');
  waitMillis=2000;
 } else if(this.waitForAddMe=="add") {
  if(popDialog) {
   var a=nHtml.FindByXPath(popDialog,".//a[contains(@href,'/addfriend.php')]");
   if(!a) {
    // maybe we've already added this friend.
    var close=nHtml.FindByAttr(popDialog,"input","name","close");
    if(close) {
     this.waitForAddMe=null;
     GM_log('Click close on error/already added dialog');
     nHtml.Click(close);
    } else {
     GM_log('waiting for add friend popup');
    }
   } else {
    GM_log('Click addfriend');
    nHtml.Click(a);
    this.SetWaitForAddMe("message");
   }
  }
 } else if(this.waitForAddMe=="message") {
  var add=null;

  if(popDialog) {
   add=nHtml.FindByAttr(popDialog,"input","name",'connect');
   var ok=nHtml.FindByAttr(popDialog,"input","name",'ok');

   var ss=document.evaluate(".//input[@type='button']",popDialog,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
   if(ss.snapshotLength==1 && ok) {
    // maybe the you are already connected message, just click ok
    GM_log('Click ok button');
    nHtml.Click(ok);
   }
  }
  if(popDialog && add) {
   var friendAddSelect=this.GetFriendListDropDown(popDialog);

   var friendList=this.bulkAddOptions.friendList;
   if(!friendAddSelect && friendList.length>0) {
    this.ClickFriendListDropDown(document.body);
   } else {
    if(friendList.length>0) {
     this.SelectFriendListDropDown(friendAddSelect,friendList);
    }
    var newsFeed=nHtml.FindByXPath(popDialog,".//input[contains(@id,'news_feed_')]");
    if(newsFeed) newsFeed.checked=this.bulkAddOptions.newsFeed;

    var showComment=nHtml.FindByXPath(popDialog,".//a[contains(@onclick,'showMessage')]");

    if(showComment) {
     GM_log('Click show comment link');
     nHtml.Click(showComment);
    }
    var tas=popDialog.getElementsByTagName('textarea');
    if(tas.length>0) {
     tas[0].value=this.bulkAddOptions.comment;
    } else {
     GM_log('Could not find message textarea');
    }
    var messLink=nHtml.FindByXPath(popDialog,".//a[contains(@onclick,'addMsgBox')]");
    if(messLink) {
     GM_log('Click show message box link');
     nHtml.Click(messLink);
    }
    if(this.bulkAddOptions.testAddMe) {
     add=nHtml.FindByAttr(popDialog,"input","name","cancel");
    }
    if(add) {
     var delay=this.bulkAddOptions.delay;
     this.SetWaitForAddMe(null); 
     // this means that it goes into the else loop after clicking on connect which means that it ignores the "already connected" error
     // therefore i've added the code to click on already connected in the else part of the if statement
     waitMillis=1000*delay;
     var t=this;
     window.setTimeout(function() {
      GM_log('Click add friend!');
      nHtml.Click(add);
      if(t.GetAutoBulkAdd()) {
       t.CloseIFrameOnCompletion();
      }
     },this.bulkAddOptions.testAddMe?1500:0);
    }
   }
  }

  if(this.waitForAddMe=="message" && (this.waitForAddMeStarted+15000)<  ((new Date()).getTime()) ) {
   var b1=document.getElementById('dialog_button1');
   var b2=document.getElementById('dialog_button2');
   if(b1 && !b2) {
    GM_log('We are already friends with this person');
    nHtml.Click(b1);
   } else {
    // 15 secs is up, still no dialog found.
    GM_log('Skipping this user cause no add friend dialog is showing up.'+window.location.href);
   }
   this.SetWaitForAddMe(null);
  }
 } else  {
  while(1) {
   if(this.addMeUptoMax>0 && this.addMeUpto>=this.addMeUptoMax) {
    GM_log('FinishedBulkAddMe, max added:'+this.addMeUptoMax);
    return;
   }
   var a=null;
   if(this.addAsFriendButtons) {
    a=this.FindNthAddAsFriend(this.addMeUpto);
    // sometimes the list of users dialog disappears inbetween adding a friend.
    
    if(this.addMeUpto<this.addAsFriendButtons) {
     var addAsFriends=this.FindNthAddAsFriends();
     if(addAsFriends) {
      this.addAsFriendButtons=addAsFriends.snapshotLength;
     } else if(!a) {
      break;
     }
    }
   } else {
    a=this.FindNthPost(this.addMeUpto);
   }
   
   this.addMeUpto++
   if(a==null && !this.profilePageAdd) {
    if(!this.BulkAddMePrevPage()) {
     if(GM_getValue('autoBulkAdd_RemoveWhenFinished',false))
     //if(autoBulkAdd_RemoveWhenFinished)
      this.SetAutoBulkAdd(false);
     GM_log('FinishedBulkAddMe');
     return;
    }
    this.addMeUpto=0;
    waitMillis=12000;
    break;
   }
   //error checking for already connected dialog - added by VX
   var add=null;
   if(popDialog) {
    add=nHtml.FindByAttr(popDialog,"input","name",'connect');
    var ok=nHtml.FindByAttr(popDialog,"input","name",'ok');

    var ss=document.evaluate(".//input",popDialog,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
    if(ss.snapshotLength==1 && ok) {
    // maybe the you are already connected message, just click ok
     GM_log('Click ok button');
     nHtml.Click(ok);
    
/*
    //click cancel on old dialog
    popDialog=document.getElementById("pop_content");
    if(popDialog) {
     var cancel=nHtml.FindByAttr(popDialog,"input","name","cancel");
     GM_log('Cancel clicked');
     nHtml.Click(cancel);
    }
*/
    }
   }
  // finished edit by vx
//   var hasAddFriendDialog=this.HasAddFriendDialog(a);
//   var ref=hasAddFriendDialog?a.getAttribute('onclick'):a.href;

   if(this.addAsFriendButtons) {
    // We have some add as friend buttons
    if(this.profilePageAdd) {
     var content=document.getElementById('content');
     if(content)
      //a=nHtml.FindByXPath(content,".//a[contains(@href,'/addfriend.php')]");
      a=nHtml.FindByXPath(content,".//a[contains(@href,'/addfriend.php')]");
    }

    var title=nHtml.FindByXPath(a.parentNode.parentNode.parentNode,".//a[contains(@class,'UIObjectListing_Title')]");
    if(!title) continue;
    var ref=title.href;
    if(this.addMeDone[ref]) { continue; }
    this.addMeDone[ref]=1;

    nHtml.Click(a);
    waitMillis=2000;
    this.SetWaitForAddMe("message");
    var t=this;
    window.setTimeout(function() {
     t.ClickClose();
    },500);
   } else {
    var ref=a.href;
    var idm=this.idRe.exec(a.href);
    if(!idm) { continue; }

    if(this.addMeDone[ref]) { continue; }
    this.addMeDone[ref]=1;
    this.ShowAddFriendDialog(idm[1],this.IsPopupToAddFriend());
    if(!this.profilePageAdd && this.IsPopupToAddFriend()) {
     waitMillis=10000;
     this.SetWaitForAddMe(null);
    } else {
     waitMillis=100;
     this.SetWaitForAddMe("message");
    }
   }
   this.waitForAddMeStarted=(new Date()).getTime();
/*
   nHtml.Click(a);
   if(hasAddFriendDialog) {
    this.waitForAddMe="message";
   } else {
    this.waitForAddMe="add";
   }
*/
   break;
  }
 }
 if(this.bulkAddOptions.testAddMe && waitMillis<2000) { waitMillis=2000; }

 this.bulkAddMeTimeout=window.setTimeout(function() { 
  FacebookInvites.BulkAddMe(startTime);
 },waitMillis);
},

profileNameUpto:1,
OpenInFrame:function(url) {
 if(location.href.indexOf('facebook.com')<0) {
  GM_openInTab(url);
  return;
 }
 var f=document.createElement('iframe');
 f.width='100%';
 f.height=window.innerHeight*0.8;

 var name='ProfileIFrame'+this.profileNameUpto;
 f.id=name;
 f.name=name;
 f.src=url;
 document.body.insertBefore(f,document.body.childNodes[0]);
 if(this.profileNameUpto==1) {
  var script=document.createElement('script');
  script.innerHTML='function '+
   'CloseIFrameByName(name) { window.setTimeout(function() {'+
   'var f=document.getElementById(name); if(f) { f.parentNode.removeChild(f); } '+
   '},100);'+
   '}';
  document.body.appendChild(script);
 }
 this.profileNameUpto++;
},

ShowAddFriendDialog:function(uid,popupToAddFriend) {
// var addFriendClick=nHtml.FindByXPath(document,"//a[contains(@onclick,'ConnectDialog')]");
 if(popupToAddFriend && !this.profilePageAdd) {
  GM_log('Lets go to the profile page:'+uid);
  this.OpenInFrame('http://www.facebook.com/profile.php?id='+uid);

/*
  this.SetMessage('Facebook has not included the javascript to add friends from this page, have to open the profile page in a new tab.'+
   "Enable auto add on the profile page if you have not done it.");
*/
 } else {
  GM_log('showfriend:'+uid);
/* Disabled: 18/feb/2010
  var addFriendClick=nHtml.FindByXPath(document,"//a[contains(@href,'rel_dialog=1') and contains(@href,'connect.php')]");
   nHtml.Click(addFriendClick);
*/
  var a=document.createElement('script');
  // 29/nov/2009
  a.innerHTML="Dialog.bootstrap('http://www.facebook.com/ajax/profile/connect.php?profile_id="+
   uid+"&rel_dialog=1&src=top_bar&ondone_reload=0',null,false);";

//  a.innerHTML='new ConnectDialog("'+uid+'", "friend_other", null, this, 0, "", -1.000000, "", "").show();';
//  a.innerHTML='show_addfriend_dialog("'+uid+'", this, undefined, undefined, undefined, undefined, undefined, "profile_others");';
  var t=this;
  window.setTimeout(function() {
   document.body.appendChild(a);
  },5000);
 }
},

BulkAddMePrevPage:function() {
 if(!this.bulkAddOptions.doPrevPages) {
  return false;
 }
 var ss=document.evaluate("//a[contains(@onclick,'start')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

 var numberRe=/^[\\s0-9]+$/;
 var onclickRe=new RegExp("start[\"\\s]*:\\s*[\-0-9]+");
 var foundUpto=0;
 var firstPagerLink=null;

 for(var s=ss.snapshotLength-1; s>=0; s--) {
  var a=ss.snapshotItem(s);
  var oncl=a.getAttribute('onclick');
  if(!oncl) { continue; }
  if(!onclickRe.exec(oncl)) {
   continue;
  }
  if(foundUpto>=1) {
   // 2nd last link
   nHtml.Click(a);
   return true;
  }

  foundUpto++;
 }

/*
 for(var s=0; s<ss.snapshotLength; s++) {
  var a=ss.snapshotItem(s);
  var oncl=a.getAttribute('onclick');
  if(!oncl) { continue; }
  if(!onclickRe.exec(oncl)) {
   continue;
  }

  if(numberRe.exec(a.textContent)) {
   if(foundUpto>0) {
    // only 1 pager link, it must be "prev" on the 2nd page, there won't be a "first" link.
    nHtml.Click(firstPagerLink);
    return true;
   }
   break;
  }
  if(foundUpto==0) {
   firstPagerLink=a;
  }
  if(foundUpto==1) {
   // the 2nd pager link with text
   nHtml.Click(a);
   return true;
  }

  foundUpto++;
 }
*/
 GM_log('No previous link found');
 return false;
},

ClearFriendsBuffer:function() {
 this.SetMessage("0 Friends in the current list.");
 this.SaveFriendsFromHash({});
},


dataGTRe:/eng_tid":([0-9]+)/,
GetFriendIdFromLink:function(a,profileRe) {
 var ppid=a.parentNode.parentNode.id;
 var ppclass=a.parentNode.parentNode.className;
 // ignore profile links in the status update and notifications areas.
 if(ppid.indexOf('notification_')>=0 || ppclass=='status_updates' || a.parentNode.className=='fb_menu_title' || a.parentNode.id=='friend_guesser') { return null; }
 var profileM=profileRe.exec(unescape(a.href));

// <a data-gt="{&quot;engagement&quot;:{&quot;eng_type&quot;:1,&quot;eng_src&quot;:2,&quot;eng_tid&quot;:1384454335,&quot;eng_data&quot;:[]}}" href="http://www.facebook.com/Sailormmark">Mark Voll</a>
 var dataGT=a.getAttribute('data-gt');
 if(dataGT) {
  var r=this.dataGTRe.exec(dataGT);
  if(r) {
   return r[1];
  }
 }

 var id=null;
 if(profileM && profileM.length>1) {
  id=profileM[2];
 } else {
  var imgs=a.getElementsByTagName('img');
  if(imgs && imgs.length>0) {
   var imgM=this.imgRe.exec(imgs[0].src);
   if(imgM && imgM.length>1) {
    id=imgM[1];
   }
  }
 }
 return id;
},
imgRe:new RegExp("http://profile.ak.facebook.com/.*/[a-z]([0-9]+)_.*\\.jpg"),
profileRe:new RegExp('.facebook.com.*/(profile|friends).*id=([0-9]+)'), //change suggested by Austin Pereira - http://userscripts.org/topics/34741#posts-167127
GetFriendsFromPage:function(doc,profileRe) {
 var as=doc.getElementsByTagName('a');
 if(!profileRe) {
  profileRe=this.profileRe;
 }
 var friendsFromPage={};
 for(var aUpto=0; aUpto<as.length; aUpto++) {
  var a=as[aUpto];
  var id=this.GetFriendIdFromLink(a,profileRe);
  if(id && !friendsFromPage[id]) {
   GM_log('add friend to list:'+id);
   friendsFromPage[id]=1;
  }
 }

 return friendsFromPage;
},

/*
ShowAddFriendsFromPage:function() {
 var div=document.createElement('div');
 div.style.position='fixed';
 div.style.left=0;
 div.style.top=0;
 div.innerHTML="<form>"+
  "Next button: <input value='.facebook.com.*profile.php.*id=([0-9]+)' id='AddFriendsFromPageButton' />"+
  "<input type='button' value='Go' id='AddFriendsFromPageButton' /><br /></form>";
 document.body.appendChild(div);
 var AddFriendsFromPageButton=document.getElementById('AddFriendsFromPageButton');
 AddFriendsFromPageButton.addEventListener(function() {
  var re=new RegExp(document.getElementById('AddFriendsFromPageButton').value);
  FacebookInvites.GetFriendsFromPage(re,true);
 },false);
},
*/

ClickAllFacebookFriendLinks:function() {
 var ss=document.evaluate("//a[contains(@href,'.facebook.com')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var profileRe=new RegExp('facebook\.com/[^/]+$');
 var profilePhpRe=new RegExp('facebook\.com/.*profile.php.*id=');
 for(var s=0; s<ss.snapshotLength; s++) {
  var a=ss.snapshotItem(s);
  if(profileRe.exec(a.href) || profilePhpRe.exec(a.href)) {
   GM_openInTab(a.href);
  }
 }
},

// add friends from any page
AddFriendsFromPage:function(info) {
 if(info.pageUpto==undefined) {info.pageUpto=0; }
 var friends=this.GetFriendsWithApp();
 var addedFriends=0;
 var friendsFromPage=this.GetFriendsFromPage(info.doc,info.profileRe);
 for(var id in friendsFromPage) {
  if(!friends[id]) {
   friends[id]=1;
   addedFriends++;
  } 
 }

 this.SaveFriendsFromHash(friends);
 var totalFriends=0; for(var f in friends) { totalFriends++; }
 GM_log('added '+addedFriends);
 this.SetMessage("Added "+addedFriends+" to the current list, total:"+totalFriends);
 if(info.doNextPage) {
  var nextPage=nHtml.FindByXPath(document,".//a[contains(@class,'UIPager_ButtonForward')]");

  if(nextPage) {
   nHtml.Click(nextPage);
   if(nextPage.parentNode.className.indexOf('Disabled')>=0) {
    // no more next buttons
    GM_log("No more next page buttons");
    info.doNextPage=false;
   }
  } else {
   var ss=document.evaluate("//a[contains(@onclick,'b:appfriends')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
   if(ss && ss.snapshotLength>0) {
    var lastItem=ss.snapshotItem(ss.snapshotLength-1);
    var numberRe=/^[\\s0-9]+$/;
    var m=numberRe.exec(lastItem.innerHTML.trim());
    if(!m) {
     // last one is not a number so it's a "next" button.
     nHtml.Click(lastItem);
    } else {
     GM_log("No more next page buttons");
     info.doNextPage=false;
    }
   } else if(info.pageUpto==0) {
    // we can't even find a next button on the first page, let's not click 'next'
    GM_log('No next button on first page, do not do multiple pages.');
    info.doNextPage=false;
   }
  }
  if(info.doNextPage) {
   GM_log('Wait for Next button');
   window.setTimeout(function() {
    info.pageUpto++;
    FacebookInvites.AddFriendsFromPage(info);
   },2000);
  }
 }
},


//////////////////////////////////////

profileIdRe:new RegExp('id=([0-9]+)'),

IterateButtons:function(func) {
 var ss=document.evaluate(".//*[contains(@class,'appRequest')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var info={
  'func':func,
  'ss':ss,
  'sUpto':0,
//  'currentApp':'Unknown',
//  'currentAppIcon':null,
//  'currentAname':'',
//  'confirmUpto':0,
  'nthButton':0
 }
 this.IterateButtonsNext(info);
},

IterateButtonsNext:function(info) {
 var t=this;
 if(info.sUpto>=info.ss.snapshotLength) return false;
 var li=info.ss.snapshotItem(info.sUpto);
 info.sUpto++;
 if(li.className.indexOf('appRequestGroup')>0) {
  var img=nHtml.FindByXPath(li,".//a[contains(@class,'UIImageBlock_ICON_Image')]/img");
  var game=nHtml.FindByXPath(li,".//a[contains(@href,'apps.facebook')]/strong");
  info.icon=img.src;
  info.app=game.innerHTML;
  t.IterateButtonsNext(info);
  return;
 }
 
 if(li.className.indexOf('appRequestBody')<0) {
  var inp=nHtml.FindByXPath(li,".//input[contains(@name,'action')]");
  if(inp) {
   info.value=inp.value;
   info.obj=inp;
   if(!info.func(info)) {
    return;
   }
  }
 }
 t.IterateButtonsNext(info);
},

/*
IterateButtonsOld:function(func) {
 var ss=document.evaluate(
  "//*[contains(@class,'confirm_boxes') "+
  "or contains(@class,'uiHeaderTop') "+
  "or contains(@class,'mbl') "+
  "or contains(@class,'uiRequestCloseSelectorButton') "+
  "or contains(@class,'inputbutton') "+
  "or contains(@name,'actions[') "+
  "or contains(@class,'uiListVerticalItemBorder') "+
  "or @class='confirm']",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var info={
  'func':func,
  'ss':ss,
  'sUpto':0,
  'currentApp':'Unknown',
  'currentAppIcon':null,
  'currentAname':'',
  'confirmUpto':0,
  'nthButton':0
 }
 this.IterateButtonsNext(info);
},

IterateButtonsNextOld:function(info) {
 var donext=this.IterateButtonsNext2(info);
 if(donext)
  this.IterateButtonsNext(info);
},

IterateButtonsNextOld2:function(info) {
// var ss=document.evaluate("//*[contains(@class,'confirm_boxes') or contains(@class,'inputbutton') or @class='confirm']",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
// var currentApp='Unknown';
// var currentAname='';
// var confirmUpto=0;
// var nthButton=0;

 var ss=info.ss;
 if(info.sUpto>=ss.snapshotLength) { return; }

// for(var s=0; s<ss.snapshotLength; s++) {
 var sObj=ss.snapshotItem(info.sUpto);
 info.sUpto++;

 if(sObj.className.indexOf('BulkAddButton')>=0) {
  return true;
 }
 if(sObj.className=='confirm' || sObj.className.indexOf('uiListVerticalItemBorder')>=0) {
  info.nthButton=0;
  info.confirmUpto++;
  return true;
 }

 if(sObj.className.indexOf('mbl')>=0
 || sObj.className.indexOf('uiHeaderTop')>=0) {
 
  var spans=sObj.getElementsByTagName('span');
  var h3s=sObj.getElementsByTagName('H3');
  //var imgs=sObj.getElementsByTagName('img');
  var icon=nHtml.FindByXPath(sObj,".//img[contains(@class,'UIImageBlock_ICON_Image')]");
  
  if(h3s.length>0) {
   info.currentApp=h3s[0].textContent.trim();
   if(spans.length>0) {
    info.currentAname=spans[0].id;
   }
   if(icon) {
    info.currentAppIcon=[icon.src,icon.className];
   } else {
    //var is=sObj.getElementsByTagName('i');
    //if(is.length>0 && is[0].className.indexOf('ICON')>=0) {
     info.currentAppIcon=['',''];
    //}
   }
  }
  return true;
 }
 if(sObj.className.indexOf('confirm_boxes')>=0) {
  var aname=sObj.id;
  var imgs=sObj.getElementsByTagName('img');
  
  var spans=sObj.getElementsByTagName('span');
  if(spans.length>0) {
   info.currentApp=spans[0].textContent.trim();
   info.currentAname=aname;
   if(imgs.length>0 && imgs[0].className.indexOf('ICON')>=0) {
    info.currentAppIcon=[imgs[0].src,imgs[0].className];
   } else {
    var is=sObj.getElementsByTagName('i');
    if(is.length>0 && is[0].className.indexOf('ICON')>=0) {
     info.currentAppIcon=['',is[0].className];
    }
   }
  }
  return true;
 }

 var buttonValue=sObj.value;
 var parent=sObj.parentNode;
 var pmRe=RegExp('p.m ');
 while(parent
//  || (parent.className.indexOf('info')<0 && parent.tagName!="LI")
 ) {
  if(parent.tagName=='BODY') { parent=null; break; }
  if(parent.className!=undefined) {
   if(parent.className.indexOf('uiListVerticalItemBorder')>=0 && parent.tagName=="LI") {
    break;
   }
   if(parent.className.indexOf('confirm')>=0 && parent.tagName=="DIV") {
    break;
   }
   //if(parent.className.indexOf('mbl')>=0 && parent.tagName=="DIV") {break; }
  }
  parent=parent.parentNode;
 }
 var profileHref=null;
 var profileId=null;
 if(parent) {
  profileHref=nHtml.FindByXPath(parent,".//a[contains(@href,'profile.php')]");

  if(profileHref) {
   var pm=this.profileIdRe.exec(profileHref.href);
   if(pm && pm.length>1) {
    profileId=pm[1];
   }
   var nameInfo=profileHref.innerHTML.trim().split(new RegExp('\\s+'),2);
   if(buttonValue) {
    buttonValue=buttonValue.replace(profileHref.innerHTML+"'s",'');
    buttonValue=buttonValue.replace(profileHref.innerHTML,'');
    buttonValue=buttonValue.replace(nameInfo[0]+"'s",'');
    buttonValue=buttonValue.replace(nameInfo[0],'');
   } else {
    buttonValue='X';
   }
  } else {
   GM_log('No profile for button: '+sObj.value+',par:'+parent.tagName);
  }
 } else {
  GM_log('Cannot find parent to button: '+sObj.value);
 }

 if(info.currentApp=="") {
  return true;
 }
 
 var donext=false;
 if(sObj.className.indexOf('inputbutton')>=0
 || sObj.className.indexOf('uiRequestCloseSelectorButton')>=0 // X close button
 || (sObj.name && sObj.name.indexOf('actions[')>=0)) {
  donext=info.func(
   {
   'aname':info.currentAname,
   'app':info.currentApp,
   'icon':info.currentAppIcon,
   'obj':sObj,
   'info':info,
   'value':buttonValue,
   'parent':parent,
   'profileId':profileId,
   'confirmUpto':info.confirmUpto,
   'nthButton':info.nthButton
  });
 }
 info.nthButton++;

 return donext;
},
*/

IsTest:function() {
 var testBox=document.getElementById('BulkRequestsTest');
 if(!testBox) { return false;  }
 var test=testBox?testBox.checked:false;
 return test;
},

OpenInIFrame:function(url) {
 var iframe=document.createElement('iframe');
 iframe.style.display='none';
 if(url) iframe.src=url;
 document.body.appendChild(iframe);
 return iframe;
},

RemoveIFrame:function(iframe) {
 // we need to remove here with a timeout so that we're not removing while in an event of the iframe that we're removing.
 window.setTimeout(function() {
  GM_log('Remove iframe, document loaded or timed out');
  iframe.parentNode.removeChild(iframe);
 },3*1000);
},
RemoveIFrameOnLoad:function(iframe) {
 var t=this;
 window.setTimeout(function() {
  if(iframe.contentDocument) {
   iframe.contentDocument.addEventListener('load',function() {
    if(iframe) t.RemoveIFrame(iframe);
    iframe=null;
   },false);
  }
 },2000);
 window.setTimeout(function() {
  if(iframe) t.RemoveIFrame(iframe);
  iframe=null;
 },30*1000);
},     

SetBulkRequestOpenType:function(t) {
 var select=document.getElementById('BulkRequestsNewTab');
 if(!select) return;
 for(var s=0; s<select.options.length; s++) {
  var option=select.options[s];
  if(option.value==t) { select.selectedIndex=s; }
 }
},
GetBulkRequestOpenType:function() {
 return document.getElementById('BulkRequestsNewTab').value;
},

NormalClick:function(form,button,clickFunc) {
 var t=this;
 var url=form.action+'?__a=1';
 var ss=document.evaluate(".//input[@type='hidden']",form,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var data=button.name+'='+button.value+'&';
 data+='nctr[_mod]=pagelet_requests&'+
  'lsd&'+
  'post_form_id_source=AsyncRequest&'+
  '__user='+unsafeWindow.Env.user;

 for(var s=0; s<ss.snapshotLength; s++) {
  var inp=ss.snapshotItem(s);
  data+='&'+inp.name+'='+inp.value;
 }
 GM_xmlhttpRequest({
  'url':url,
  'method':'POST',
  'data':data,
  onerror:function() {
   GM_log("Failed to Open:"+url);
   t.AddAutoAcceptError("Error with:"+url);
  },
  onload:function(resp) {
   function unQuote(str) {
    return str.replace(/\\(.)/g,'$1');
   }
   var m=/goURI\(([^\]]+)\);"/.exec(resp.responseText);
   var newurl=unQuote(unQuote(m[1]));
   newurl=newurl.replace(/^"/,''); 
   newurl=newurl.replace(/"$/,''); 
   clickFunc(newurl);
   
   t.AddAutoAcceptError("Visited:"+newurl);
   GM_log("Opened ok:"+newurl);
  }
 });

/* 
 form.action=form.action+'?__a=1';
 form.target='_blank';
 
 form.onsubmit='';
 
 SendNormalClick();
 function AppendHidden(n,v) { 
  var inp=document.createElement('input');
  inp.type='hidden';
  inp.name=n;
  inp.value=v;
  form.appendChild(inp);
 }
 AppendHidden('actions[accept]','Aceptar');
 AppendHidden('nctr[_mod]','pagelet_requests');
 AppendHidden('lsd','');
 AppendHidden('post_form_id_source','AsyncRequest');
 AppendHidden('__user',unsafeWindow.Env.user);
*/
},


ClickAutoAcceptButton:function(obj,normalClick) {
 var t=this;
 if(this.IsTest()) {
  obj.style.border='3px solid #f00';
  return;
 }

 var t=this;
 //var onclick=obj.getAttribute('onclick');
 var openType=this.GetBulkRequestOpenType();
 if(openType=="") {
  normalClick=true;
 }
//   var platformRe=new RegExp("click_add_platform_app[^;]+'(http[^']+)'");
  var platformRe=/actions\[(http[^\]]+)\]/;
  var m=platformRe.exec(obj.name);
  if(m) {
   var url=m[1].replace('&amp;','&');
   GM_log("Opening:"+url);
   if(openType=='hidden') {
    GM_xmlhttpRequest({
     'url':url,
     'method':'GET',
     onerror:function() {
      GM_log("Failed to Open:"+url);
      t.AddAutoAcceptError("Error with:"+url);
     },
     onload:function() {
      t.AddAutoAcceptError("Visited:"+url);
      GM_log("Opened ok:"+url);
     }
    });
   } else if(openType=='iframe') {
    var iframe=this.OpenInIFrame(url);
    iframe.style.display='block';
    iframe.width='100%';
    iframe.height='400';
    t.RemoveIFrameOnLoad(iframe);
   } else {
    GM_openInTab(url);
   }
  }
  
  var p=obj.parentNode;
  var form=null;
  while(p.tagName!="BODY" && p) {
   if(p.tagName=="FORM") { form=p; break; }
   p=p.parentNode;
  }

//  if(normalClick) {

   if(form) {
    t.NormalClick(form,obj,function(newurl) {
     if(openType=='hidden') {
      GM_xmlhttpRequest({
       'url':newurl,
       'method':'GET',
       onload:function() {
        GM_log('visited url without showing:'+newurl);
       }
      });
      
     } else if(openType=='iframe') {
       var iframe=this.OpenInIFrame();
       iframe.src=newurl;
       window.setTimeout(function() {
        iframe.parentNode.removeChild(iframe);
       },5000);
     } else  if(openType=='') {
      GM_openInTab(newurl);
     }
    });

//GM_log('form:'+unsafeWindow.Env.user);

//    form.submit();
//    nHtml.Click(obj);
   } else {
    GM_log('Error:  cannot find form with button'+obj.innerHTML);
   }
/*   
  } else if(openType=='hidden' || openType=='tab') {
   var ss=document.evaluate(".//input",form,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
   
   var dataStr='';
   for(var s=0; s<ss.snapshotLength; s++) {
    var inp=ss.snapshotItem(s);
    if(inp.type.toUpperCase()=="SUBMIT") {
     if(inp.name!=obj.name) {
      continue;
     }
    }
    dataStr+=escape(inp.name)+'='+escape(inp.value)+'&';
   }

   GM_xmlhttpRequest({
    'url':form.action,
    'headers':{'Content-type':'application/x-www-form-urlencoded'},
    'method':'POST',
    'data':dataStr
   });
  } else {
   var iframe=this.OpenInIFrame();
   iframe.contentDocument.body.innerHTML=
    "<base href='"+document.location.href+
    "' /><form method='"+form.method+"' action='"+form.action+"'>"+
    form.innerHTML+"</form>";
   iframe.contentDocument.forms[0].submit();
   window.setTimeout(function() {
    iframe.parentNode.removeChild(iframe);
   },5000);
  }
 */ 
},


GetMaxClicks:function() {
 var maxClicks=document.getElementById('BulkRequestsMaxClicks');
 if(!maxClicks) return 0;
 return maxClicks.value;
}, 

GetClickDelay:function() {
 var maxClicks=document.getElementById('BulkRequestsDelay');
 if(!maxClicks) return null;
 var v=parseInt(maxClicks.value);
 if(isNaN(v)) return null;
 return v;
},

ClickAutoAccept:function(e) {
 var target=e.target;
 var appInfo=target.parentNode.id.split('_',2);
 var app=appInfo[1];
 app=unescape(app);
 var nth=null;
 var button=null;
 var byText=null;

 if(target.id=='BulkClickByTextButton') {
  byText=document.getElementById('BulkClickByText').value.toLowerCase().trim();
  if(byText=="") return;
 } else {
  if(e.target.value.substring(e.target.value.length-6)=='button') {
   var nthM=/^([0-9]+)/.exec(e.target.value);
   nth=parseInt(nthM[1])-1;
  } else {
   var buttonInfo=e.target.value.split(' x ',2);
   button=buttonInfo[1];
  }
 }
 var friendsClicked={};
 if(!e.buttonUpto) { e.buttonUpto=0; }
 var t=this;
 this.IterateButtons(function(info) {
  t.SkipButton();
  var ok=false;
  var maxClicks=t.GetMaxClicks();
   if(maxClicks>0 && e.buttonUpto>=maxClicks) {
   return false;
  }
   if(byText!=null && info.value.toLowerCase().indexOf(byText)>=0) {
   ok=true;
  }

  if(( 
   (app==t.allAppsId || info.app==app) 
   && (info.value==button || info.nthButton==nth)
  ) || ok) {
   if(info.profileId) {
    friendsClicked[info.profileId]=1;
   }

   /*
   var friendList=document.getElementById('AcceptFriendList').value;
   var friendAddSelect=t.GetFriendListDropDown(info.parent);
   if(friendList.length>0) {
    t.ClickFriendListDropDown(info.parent);
    window.setTimeout(function() {
     // wait for the drop down.
     friendAddSelect=t.GetFriendListDropDown(info.parent);
     if(!friendAddSelect) { 
      GM_log("Cannot find friend list drop down!"); 
     }
     if(friendAddSelect) {
      // we have a drop down
      t.SelectFriendListDropDown(friendAddSelect,friendList);
     }
     window.setTimeout(function() {
      t.ClickAutoAcceptButton(info.obj,true);
      t.IterateButtonsNext(info.info);
     },1000);
    },3000);
    e.buttonUpto++;
    return false;
   }
   */

   var openType=t.GetBulkRequestOpenType();

//   var friendMenu=nHtml.FindByXPath(info.parent,".//span[@class='FriendAddingTool_Menu']");
   
//   if(openType=="" || friendMenu) {
   var currentButtonUpto=e.buttonUpto;
   var currentObj=info.obj;
   var wait;
   var clickDelay=t.GetClickDelay();
   if(clickDelay!=null) {
    wait=clickDelay*1000*e.buttonUpto;
   }
   else if(openType=='hidden') wait=800*e.buttonUpto;
   else wait=3*1000*e.buttonUpto;
   
   window.setTimeout(function() {
    t.SetAutoAcceptStatus("Clicking button: "+(currentButtonUpto+1));
    t.ClickAutoAcceptButton(currentObj,false);
    t.IterateButtonsNext(info);
   },wait);
   e.buttonUpto++;
   return false;
  }
  return true;
 });
 this.SaveFriendsFromHash(friendsClicked);
},

ClickBlockButton:function() {
 var blockDiv=nHtml.FindByXPath(document,".//div[contains(@class,'dialog_buttons')]");
 if(!blockDiv) { return; }
 var block=nHtml.FindByXPath(blockDiv,".//input[@type='button']");
 if(block) {
  GM_log('block button pressed');
  nHtml.Click(block);
  return true;
 }
 return false;
},
SetBlockUpto:function(clicked,s) {
 var clickedStr='';
 for(var click in clicked) {
  clickedStr+=click+"###";
 }
 GM_setValue('blockLinkClicked',clickedStr);
 GM_setValue('blockLinkUpto',s);
},
GetBlockUpto:function(clicked) {
 var clickedStr=GM_getValue('blockLinkClicked','');
 var clickedArr=clickedStr.split('###');
 for(var c=0; c<clickedArr.length; c++) {
  clicked[clickedArr[c]]=1;
 }
 return GM_getValue('blockLinkUpto',-1);
},
BlockAllApps:function() {
 var clicked={};
 var s=this.GetBlockUpto(clicked);
 if(s<0) { return; }

 var stopBlockAll=document.getElementById('StopBlockAllApps');
 if(!stopBlockAll) {
  this.SetMessage("<a id='StopBlockAllApps'>Stop block all applications</a>");
  stopBlockAll=document.getElementById('StopBlockAllApps');
  stopBlockAll.addEventListener('click',function() {
   FacebookInvites.SetBlockUpto({},-1);
  },false);
 }

 var s=0;

 var test=this.IsTest();
 var ss=document.evaluate("//a[contains(@onclick,'block_app_dialog')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var obj=null;
 while(1) {
  if(s>=ss.snapshotLength) {
   this.SetMessage('Done block all');
   this.SetBlockUpto({},-1);
   window.setTimeout(function() {
    this.ClickBlockButton();
   },5000);
   return;
  }
  obj=ss.snapshotItem(s);
  var onclick=obj.getAttribute('onclick');
  if(!clicked[onclick]) {
   clicked[onclick]=1;
   break;
  }
  s++;
  continue;
 }
 this.SetBlockUpto(clicked,s); 
 window.setTimeout(function() {
  GM_log('click block:'+obj.getAttribute('onclick'));
  if(test) {
   obj.style.border='3px solid #f00';
  } else {
   nHtml.Click(obj);
  }
  window.setTimeout(function() {
   if(!FacebookInvites.ClickBlockButton()) {
    // clicking the block button will reload the page, we will run again when the page reloads.
    FacebookInvites.BlockAllApps();
   }
  },3000);
 },1000);
},

GetDivToggle:function(mDiv,settingName,def) {
 var bulkReqToggle=document.createElement('a');
 bulkReqToggle.href='javascript:;';
 var t=this;
 bulkReqToggle.addEventListener('click',function() {
  var show=GM_getValue(settingName,def);
  var show=show?false:true;
  GM_setValue(settingName,show);
  mDiv.style.display=show?'block':'none';
 },false);
 mDiv.style.display=GM_getValue(settingName,def)?'block':'none';
 return bulkReqToggle;
},

/*
SwitchConfirmDiv:function(nthButtonsToggle,nth) {   
 nthButtonsToggle.innerHTML=nth?"Switch to named buttons":"Switch to nth buttons";
 document.getElementById("nthConfirmDiv").style.display=nth?"block":"none";
 document.getElementById("nameConfirmDiv").style.display=nth?"none":"block";
 GM_setValue('showNthDiv',nth);
},
*/

GetLastClicked:function(target) {
 var lastClickedArr=GM_getValue("LastClicked","").split(',');
 var lastClicked={};
 for(var i=0; i<lastClickedArr.length; i+=2) {
  if(lastClickedArr[i]=="") continue;
  lastClicked[lastClickedArr[i]]=lastClickedArr[i+1];
 }
 return lastClicked;
},
SaveLastClicked:function(target) {
 var lastClicked=this.GetLastClicked();
 var appInfo=target.parentNode.id.split('_',2);
// var hash=target.parentNode.getAttribute('appHash');
 var hash=target.parentNode.getAttribute('appName');
 var valInfo=target.value.split(' x ');
 var val=target.value;
 if(valInfo.length>=2) val=valInfo[1];
 lastClicked[hash]=this.GetBulkRequestOpenType()+"#"+val;
// lastClicked[hash]=this.GetBulkRequestOpenType()+"#"+target.value; 
 
 var lastClickedArr=[];
 for(var id in lastClicked) {
  lastClickedArr.push(id);
  lastClickedArr.push(lastClicked[id].replace(/,/g,''));
 }
 GM_setValue("LastClicked",lastClickedArr.join(','));
},

ClickAutoAccepts:function(buttons) {
 var buttonUpto=0;
 for(var b=0; b<buttons.length; b++) {
  var buttonInfo=buttons[b];
  var button=buttonInfo.button;
  var e={'target':button,'buttonUpto':buttonUpto};
  this.SetBulkRequestOpenType(buttonInfo.openType);
  this.ClickAutoAccept(e);
  buttonUpto=e.buttonUpto;
 }
},
SetAutoAcceptStatus:function(html) {
 var d=document.getElementById('AutoAcceptStatus');
 if(d) d.innerHTML=html;
},  
AddAutoAcceptError:function(html) {
 var d=document.getElementById('AutoAcceptError');
 if(!d) return;

 var div=document.createElement('div');
 div.innerHTML=html;
 d.appendChild(div);
},  

ClickAllMoreRequests:function() {
 var ss=document.evaluate(".//a[contains(@class,'uiMorePagerPrimary')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 var clicked=0;
 for(var s=0; s<ss.snapshotLength; s++) {
  var a=ss.snapshotItem(s);
  nHtml.Click(a);
  clicked++;
 }
 return clicked;
},

// add a link to auto accept all the friend requests on the page.
AddAutoAccept:function() {
 var event=document.getElementById('globalContainer');
 if(event) {
  event=event.childNodes[0];
 }

 /*
 var event=document.getElementById('friend_suggestion');
 if(!event) event=document.getElementById('friend_add');
 if(!event) event=document.getElementById('event_invite');
 */
 if(!event) { return; }
 var mDiv=document.getElementById('BulkRequestsDiv');
 if(mDiv) { return; }
 
 this.ClickAllMoreRequests();
 
 mDiv=document.createElement('div');
 var buttonTypes={};
 var buttonsCount={};
 var maxButtons={};
 var appTypes={};
 this.IterateButtons(function(info) {
  var app=info.app;
  var buttonValue=info.value;
  appTypes[app]={'hash':info.aname,'icon':info.icon};
  if(buttonTypes[app]==undefined) {
   buttonTypes[app]={};
  }
  if(buttonTypes[app][buttonValue]==undefined) {
   buttonTypes[app][buttonValue]=0; 
  }
  buttonTypes[app][buttonValue]++;
  appTypes[FacebookInvites.allAppsId]={'hash':'','desc':'<b>All Applications</b>'};
  var buttonUpto=info.nthButton;
  if(!maxButtons[app] || buttonUpto>=maxButtons[app]) { 
   maxButtons[app]=buttonUpto+1; 
   if(!maxButtons[FacebookInvites.allAppsId] || buttonUpto>=maxButtons[FacebookInvites.allAppsId])
    maxButtons[FacebookInvites.allAppsId]=buttonUpto+1;
  }
  return true;
 });

 var bulkReqToggle=this.GetDivToggle(mDiv,'ShowBulkReqClick',true);
 bulkReqToggle.innerHTML='<b>'+this.scriptName+': Show bulk click requests block</b>';

 var toggleErrorDivJs='document.getElementById("AutoAcceptError").style.display'+
  '=document.getElementById("AutoAcceptError").style.display=="none"'+
  '?"block":"none"';
 
 mDiv.id='BulkRequestsDiv';
 var delay=this.GetClickDelay();
 if(delay==null) delay='';
 mDiv.innerHTML=
  "You will have to press reload here to see the changes afterwards.<br />"+
  "<input type='checkbox' id='BulkRequestsTest' />Test only(only highlights the buttons to be clicked)<br />"+
  "<select id='BulkRequestsNewTab'><option value='' selected>Normal Click</option><option value='hidden' >Hidden (fastest but may not work)</option></select> when clicking on something (Some applications need this to confirm that you've accepted)<br />"+
//  <option value='tab' >Open up a new tab</option>
//  <option value='iframe'>Open up in iframe (at the bottom of page)</option>
  "Only click <input size='3' id='BulkRequestsMaxClicks' /> buttons at a time, <input size='3' id='BulkRequestsDelay' value='"+delay+"' />secs inbetween each click.<br />"+
//  "Friend list: <input type='text' id='AcceptFriendList' /> (only for when you're accepting friends)<br />"+
//  "<input id='BulkClickByTextButton' type='button' value='Click all buttons with this text:' /> <input type='text' id='BulkClickByText' /> <br />"+
  "<div id='AutoAcceptStatus'></div>"+
  "<a href='javascript:;' onclick='"+toggleErrorDivJs+"'>Show messages</a><br /><div id='AutoAcceptError' style='display: none'></div>"
  ;
/*
//  "<a id='nthButtonsToggle' style='font-size: 14pt' href='javascript:;'></a><br />"
 var nthButtonsToggle=nHtml.FindByXPath(mDiv,"//a[@id='nthButtonsToggle']");
 if(nthButtonsToggle) {
  var t=this;
  nthButtonsToggle.addEventListener('click',function() {
   var show=document.getElementById("nthConfirmDiv").style.display=='none'?true:false;
   t.SwitchConfirmDiv(nthButtonsToggle,show);
  },false);
 }
*/
 var t=this;
 mDiv.appendChild(document.createElement('br'));
 var blockAllApps=document.createElement('A');
 blockAllApps.innerHTML="Click on all 'Block application' links (becareful, facebook has no list of blocked applications)";
 blockAllApps.addEventListener('click',function() {
  FacebookInvites.SetBlockUpto({},0); 
  FacebookInvites.BlockAllApps();
 },false);
 mDiv.appendChild(blockAllApps);
 mDiv.appendChild(document.createElement('br'));
 mDiv.appendChild(document.createElement('br'));
 
 var lastClickedButtons=[];
 var clickLastClicked=document.createElement('A');
 clickLastClicked.innerHTML='Click the same buttons you clicked last time(all the green ones)';
 clickLastClicked.addEventListener('click',function() {
  t.ClickAutoAccepts(lastClickedButtons);
 },false);
 mDiv.appendChild(clickLastClicked);
 mDiv.appendChild(document.createElement('br'));
 mDiv.appendChild(document.createElement('br'));

 var nthDiv=document.createElement('div');
 nthDiv.id='nthConfirmDiv';
 nthDiv.style.display='none';
 var nthTable=document.createElement('table');
 nthDiv.appendChild(nthTable);
 
 var appsArr=[];
 for(var app in appTypes) {
  appsArr.push(app);
 }

 var lastClicked=this.GetLastClicked();
 appsArr=appsArr.sort();
 var nameDiv=document.createElement('div');
 nameDiv.id='nameConfirmDiv';
 
 var nameTable=document.createElement('table');
 nameDiv.appendChild(nameTable);
 var lastClickedColor='#0f0';
 

 for(var a=0; a<appsArr.length; a++) {
  var app=appsArr[a];
  var d=document.createElement('div');
  var appInfo=appTypes[app];
  if(appInfo.desc) { appDesc=appInfo.desc; }
  else { appDesc=app; }
  var appLink='<table border=0><tr><td>';
  if(appInfo.icon) {
   if(appInfo.icon[0]!="") {
    appLink+="<img src='"+appInfo.icon[0]+"' />";
   } else {
    appLink+="<i class='"+appInfo.icon[1]+"'></i>";
   }
  }
  
  appLink+="</td><td><a href='#"+appInfo.hash+"'>"+appDesc+"</a></td></tr></table>";
  var hash=appTypes[app].hash;
  var divId='ButtonApp_'+escape(app);

//  var lastClickedForAppStr=lastClicked[hash];
  var lastClickedForAppStr=lastClicked[app];
  var lastClickedForApp=null;
  var lastClickedOpenType='hidden';

  if(lastClickedForAppStr) {
   var lastClickedForAppInfo=lastClickedForAppStr.split('#');
   if(lastClickedForAppInfo.length==1) {
    lastClickedForApp=lastClickedForAppInfo[0];
   } else {
    lastClickedOpenType=lastClickedForAppInfo[0];
    lastClickedForApp=lastClickedForAppInfo[1];
   }
  }
  d.id=divId;
//  d.setAttribute('appHash',hash);
  d.setAttribute('appName',app);
  var buttons=buttonTypes[app];
  
  var buttonsAdded=0;
  if(buttons) {
   for(var button in buttons) {
    var count=buttons[button];
    //if(count<=1) { continue; }
    var inp=document.createElement('input');
    inp.type='button';
    inp.className='inputbutton BulkAddButton';
    inp.value=count+" x "+button;
 

    if(lastClickedForApp && button==lastClickedForApp) {
     inp.style.color=lastClickedColor;
     lastClickedButtons.push({'button':inp,'openType':lastClickedOpenType});
    }
    inp.addEventListener('click',function(e) {
     t.SaveLastClicked(e.target);
     t.ClickAutoAccept(e);
    },false);
    d.appendChild(inp);
    d.appendChild(document.createTextNode(' '));
    buttonsAdded++;
   }
  }

  var nthApp=document.createElement('DIV');
  nthApp.id=divId;
//  nthApp.setAttribute('appHash',hash);
  nthApp.setAttribute('appName',app);
  //nthApp.innerHTML=appDesc;
  var maxButton=maxButtons[app];
  for(var b=0; b<maxButton; b++) {
   var inp=document.createElement('input');
   inp.type='button';
   inp.className='inputbutton BulkAddButton';
   
   var suffix="th";
   var b10=b%10;
   if(b10==0) { suffix="st"; }
   else if(b10==1) { suffix="nd"; }
   else if(b10==2) { suffix="rd"; }
   inp.value=(b+1)+suffix+" button";
   if(lastClickedForApp && inp.value==lastClickedForApp) {
    inp.style.color=lastClickedColor;
    lastClickedButtons.push({'button':inp,'openType':lastClickedOpenType});
   }
   inp.addEventListener('click',function(e) {
    t.SaveLastClicked(e.target);
    t.ClickAutoAccept(e);
   },false);
   nthApp.appendChild(inp);
   nthApp.appendChild(document.createTextNode(' '));
  }
//  nthApp.appendChild(document.createElement('BR'));

  var nthRow=nthTable.insertRow(-1);
  var desccell=nthRow.insertCell(-1);
  desccell.innerHTML=appLink;
  desccell.style.width='200px';
  desccell.style.verticalAlign='top';
  nthRow.insertCell(-1).appendChild(nthApp);
  
  //nthDiv.appendChild(nthApp);

  if(buttonsAdded>0) {
   var nameRow=nameTable.insertRow(-1);
   var desccell=nameRow.insertCell(-1);
   desccell.style.width='200px';
   desccell.style.verticalAlign='top';
   desccell.innerHTML=appLink;
   nameRow.insertCell(-1).appendChild(d);
  }
 }
 mDiv.appendChild(nameDiv);
 mDiv.appendChild(nthDiv);


 
 event.parentNode.insertBefore(bulkReqToggle,event);
 event.parentNode.insertBefore(mDiv,event);

 
// this.SwitchConfirmDiv(nthButtonsToggle,GM_getValue('showNthDiv',false));
/* 
 var BulkClickByTextButton=document.getElementById('BulkClickByTextButton');
 BulkClickByTextButton.addEventListener('click',function(e) {
  FacebookInvites.ClickAutoAccept(e);
 },false);
*/
},


// auto bulk add when we get to the profile page.
SetAutoBulkAdd:function(enabled) {
 GM_setValue('autoBulkAdd_Enabled',enabled);
 if(document.getElementById('bulkAddComment')) {
  GM_setValue('autoBulkAdd_Comment',document.getElementById('bulkAddComment').value);
  GM_setValue('autoBulkAdd_FriendList',document.getElementById('bulkAddFriendList').value);
  GM_setValue('autoBulkAdd_Test',document.getElementById('testBulkAddMeButton').checked);

 }
},

GetAutoBulkAdd:function() {
 var enabled=GM_getValue('autoBulkAdd_Enabled',false);
 if(enabled && document.getElementById('bulkAddComment')) {
  document.getElementById('bulkAddComment').value=GM_getValue('autoBulkAdd_Comment','');
  document.getElementById('bulkAddFriendList').value=GM_getValue('autoBulkAdd_FriendList','');
  document.getElementById('testBulkAddMeButton').checked=GM_getValue('autoBulkAdd_Test',false);
 }
 return enabled;
},






//////////////////////////

UncheckShowNews:function() {
 var chks = document.evaluate("/html[@id='facebook']/body//input[@value='news_feed']",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 for (var i =0; i < chks.snapshotLength; i++) { chks.snapshotItem(i).checked=false;}
},

// click the "close" buttons 
ClickClose:function() {
/*
 var db=document.getElementById('dialog_buttons');
 if(!db) db=nHtml.FindByXPath(document,"//div[contains(@class,'dialog_buttons')]");
 if(!db) return;
*/
 var popDialogss=document.evaluate(".//div[contains(@id,'pop_content')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
 for(var s=0; s<popDialogss.snapshotLength; s++) {
  var pop_content=popDialogss.snapshotItem(s);
  
  var chks = document.evaluate(".//input",pop_content,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

  if(chks.snapshotLength==1) {
   // only one button
   var inp=chks.snapshotItem(0);
   if(inp.name=='close') {
    // only one button and it says "close"
    GM_log('Close button clicked');
    nHtml.Click(inp);
   }
  }
 }
},


profilePageAdd:false,
CheckPage:function() {
 var t=this;
 try{
  if(unsafeWindow.top && unsafeWindow.top.joinRecentLoadedLink!=undefined && unsafeWindow.joinRecentLoadedLink==undefined) {
   window.setTimeout(function() {
    if(unsafeWindow.top) {
     unsafeWindow.top.joinRecentLoadedLink();
    }
   },5000);
   return;
  }
 }catch(e) {
  //GM_log('no permission probably:'+e);
//  return;
 }

 var href=document.location.href+document.location.hash;
 var ok=false;
// if(href.indexOf("/reqs.php")>=0) {
 if(href.indexOf("/games")>=0) {
  this.UncheckShowNews();
  t.AddAutoAccept();
 }
// if(href.indexOf("/profile.php")>=0) {
 var profileStream=document.getElementById('profile_stream_container');
 var okToAddAsFriend=false;
 
 if(document.location.href.indexOf('/iframe/')<0) {
  if(document.getElementById('pagelet_header_personal') && !profileStream) {
   // we're not friends yet.
   this.AddMeLinkAnyPage();
   var addLink=nHtml.FindByXPath(document,".//a[contains(@href,'rel_dialog=1')]");

   if(this.GetAutoBulkAdd() && !this.profilePageAdd) {
    // auto add this person.
    this.profilePageAdd=true;
    if(addLink) {
     this.addMeUptoMax=1;
     this.StartBulkAddMe();
    }
    okToAddAsFriend=true;
   }
  } 

  if(!okToAddAsFriend && this.GetAutoBulkAdd()) {
   // we're currently friends, let's close
   this.CloseIFrameOnCompletion();
  }
 }

// if(href.indexOf("/social_graph.php")>=0) { //change suggested by Lox Myth - see http://userscripts.org/topics/34630#posts-166598

 if(href.indexOf("friends_using_app")>=0) { 
  ok=this.AddFriendsWithApp();
 } else {
  this.AddMeLink();
  ok=this.InviteFriends();
 }
 this.AddAsFriendLinks();
 window.setTimeout(function() { FacebookInvites.CheckPage() },5000);
}


};

GM_registerMenuCommand('FB Invites - Show Bulk add friends dialog',function() {
 FacebookInvites.AddMeLinkAnyPage();
});

GM_registerMenuCommand('FB Invites - Add all the friends on this page',function() {
 var content=document.getElementById('content');
 if(!content) content=document.body;
 FacebookInvites.AddFriendsFromPage({'doc':content,'doNextPage':true});
});
GM_registerMenuCommand('FB Invites - Clear the current list of friends',function() {
 FacebookInvites.ClearFriendsBuffer();
});
GM_registerMenuCommand('FB Invites - Detach the block if it is not visible',function() {
 FacebookInvites.DetachDiv();
});

if(location.href.indexOf('facebook.com')<0) {
 GM_registerMenuCommand('FB Invites - Open all facebook friend links here in new tabs',function() {
  FacebookInvites.ClickAllFacebookFriendLinks();
 });
}
if(location.href.indexOf('apps.facebook.com') > 0) {
 GM_registerMenuCommand('FB Invites - Update friends using this application',function() {
  FacebookInvites.VisitFriendsList();
 });
}

window.addEventListener("load", function(e) {
 window.GetFacebookInvites = function() {
  return FacebookInvites;
 }
 FacebookInvites.CheckPage();
 FacebookInvites.BlockAllApps();
/*
 window.setTimeout(function() {
var uid="100000214449696";
  var a=document.createElement('script');
  // 29/nov/2009
  a.innerHTML="Dialog.bootstrap('http://www.facebook.com/ajax/profile/connect.php?profile_id="+
   uid+"&rel_dialog=1&src=top_bar&ondone_reload=0',null,false);";
  document.body.appendChild(a);
},2000);
*/
}, false);

// join recent friends.
// test nth block, block buttons.

0 comments:

Post a Comment