//////////////////////////////////////////
//           ajaximrpg 5.00             //
//       AJAX Instant Messenger         //
//  Copyright (c) 2006-2008, 2010-2012  //
//      Do not remove this notice       //
//////////////////////////////////////////

// See config.js for configuration options //

/**
 * Global variables used through the script
 **/
var user='';
var pass='';
var curSelected='';
var loggedIn=false;
var titlebarBlinker=false;
var defaultTitle = document.title=(siteName.length>0?siteName:document.title);
var blinkerTimer;
var pingTimer=null;
var plugins = {};
var newWin, newWinRcvd;
var windowButtons;
var smilies = [];
var soundManager;
var installCheckComplete=false;
var hrefPath = window.location.href;
hrefPath = hrefPath.substring(0, hrefPath.length - window.location.search.length);
hrefPath = hrefPath.substring(0, hrefPath.lastIndexOf('/')+1);

/**
 * Before the window is 'unloaded', confirm the user wants to leave
 *
 * @author Joshua Gross
 **/
window.onbeforeunload = function(event) {
   event = event || window.event;
   if (event && loggedIn) {
      var text = Languages.get('onunload');
      event.returnValue = text;
      window.onbeforeunload = function() { };
      return text;
   }
};

/**
 * After all content and images for the web page is loaded,
 * run some functions
 *
 * @author Joshua Gross
 **/
window.onload = function() {
   Windows.addObserver({ onResize: Chatroom.handleResize });
   Windows.addObserver({ onClose: Chatroom.handleClose });
   Windows.addObserver({ onMaximize: Chatroom.handleResize });
   Windows.addObserver({ onMinimize: IM.handleMinimize });
   
   // initialize the sound manager
   soundManager = new SoundManager('soundmanager2/soundmanager2.swf');
   soundManager.onload = function() {
      soundManager.createSound({id: 'msg_in', url: './sounds/msg_in.mp3', autoLoad: true});
      soundManager.createSound({id: 'msg_out', url: './sounds/msg_out.mp3', autoLoad: true});
      soundManager.play('msg_out');
   };
   soundManager.beginDelayedInit();

   // attach event
   //  before window is unloaded, remove sound manager
   Element.observe(window, 'beforeunload', soundManager.destruct);
   
   // on window unload, logout the user
   Event.observe(window, 'unload', function() { if (loggedIn) System.logout(); });

   // replace status images with theme-based images
   $('statusList').getElementsBySelector('img').each(function(el) {
      el.src = el.src.replace(/images/g, 'themes/' + theme);
   });

   // initialize Context Menus
   Context.loaded();
   
   // hook mousedown for status list
   var dOMD = (document.onmousedown ? document.onmousedown : new Function());
   document.onmousedown = window.onmousedown = function(e) {
      showHide(e);
      dOMD(e);
   };

   // install database, then login
   var installPassword = hex_md5('');
   var xhConn = new XHConn();
   xhConn.connect(pingTo, "POST", "call=checkinstall&password="+installPassword, function(xh) {
      installCheckComplete = true;
      if ($('check_install')) {
         Windows.close('check_install');
      }
      if (xh.outputText.isJSON()) {
         var response = xh.outputText.evalJSON(true);
         if (response.db == 'ready') {
            login();
         } else {
            // load admin.js file
            var s = document.createElement('script');
            s.setAttribute('src', 'js/admin.js?' + (new Date()).getTime());
            s.setAttribute('type', 'text/javascript');
            document.getElementsByTagName('head').item(0).appendChild(s);
            setTimeout(function() {
               // run installation
               if (response.password_disabled) {
                  AdminWindows.installDisabled();
               } else if (response.auth) {
                  Admin.installPassword = installPassword;
                  AdminWindows.admin();
               } else if (response.password) {
                  AdminWindows.install();
               } else {
                  AdminWindows.corrupt(Languages.get('error'), 'ajax_im.js/checkinstall (JSON):', xh);
               }
            }, 500);
         }
      } else {
         // load admin.js file
         var s = document.createElement('script');
         s.setAttribute('src', 'js/admin.js?' + (new Date()).getTime());
         s.setAttribute('type', 'text/javascript');
         document.getElementsByTagName('head').item(0).appendChild(s);
         setTimeout(function() {
            AdminWindows.corrupt(Languages.get('error'), 'ajax_im.js/checkinstall (PHP):', xh);
         }, 500);
      }
   });

   // show and center "checking installation" dialog while waiting
   setTimeout(function() { if (!installCheckComplete) { Dialogs.check(); } }, 1000);
};

/**
 * Initial login. 
 *
 * @author Daniel Howard
 **/
function login() {
   if (haveLobby) {
      System.login('anonymous', 'anonymous');
   } else {
      Dialogs.login();
   }
}

/**
 * After all content for the web page is loaded,
 * load some more stuff.
 *
 * @author Joshua Gross
 **/
document.observe('dom:loaded', function() {
   var getEmoteHTML = new XHConn();
   getEmoteHTML.connect('themes/' + theme +'/emoticons/emoticons.html', 'GET', '', function(xh) {
      document.body.innerHTML += xh.responseText;

      var getEmoteJS = new XHConn();
      getEmoteJS.connect('themes/' + theme +'/emoticons/emoticons.js', 'GET', '', function(xh) {
         window.smilies = xh.responseText.evalJSON(true);
      });
   });
   
   // load language file
   var s = document.createElement('script');
   s.src = 'languages/' + languageOptions[0][0] + '/lang.js?' + (new Date()).getTime();
   s.type = 'text/javascript';
   document.getElementsByTagName('head').item(0).appendChild(s);

   // if lingo is enabled
   if (useLingo) {
      // load the lingo file
      var l = document.createElement('script');
      l.src = 'languages/' + languageOptions[0][0] + '/lingo.js?' + (new Date()).getTime();
      l.type = 'text/javascript';
      document.getElementsByTagName('head').item(0).appendChild(l);
   }
   
   // if there is more than one language installed on the server, show them as options
   if (languageOptions.length > 1) {
      for(var i=0; i<languageOptions.length; i++)
         $('languageList').innerHTML += '<a href="#" onclick="Languages.load(\'' + languageOptions[i][0] + '\');return false;">' + languageOptions[i][1] + '</a> | ';

      $('languageList').innerHTML = $('languageList').innerHTML.substring(0, $('languageList').innerHTML.length - 3);
   }
});

/**
 * Clear the value of input elements
 *
 * @author Joshua Gross
 **/
function clearInputs() {
   var formInputs = document.getElementsByTagName('input');
   for (var i=0; i < formInputs.length; i++) {
      if (formInputs[i].type == 'text' || formInputs[i].type == 'password') {
         formInputs[i].value = '';
      }
   }
}

/**
 * This function is ran everytime the mouse is clicked
 *
 * @arguments
 *   event - passed by browser
 *
 * @author Joshua Gross
 * @update Benjamin Hutchins
 **/
function showHide(event) {
   var target;
   event = event || window.event;
   if (document.all) {
      target = event.srcElement;
   } else {
      target = event.target;
   }
  if (!target) {return;}
  if (loggedIn &&
      target.id != 'statusList' &&
      target.id != 'fontsList' &&
      target.id != 'statusSettings' &&
      target.id != 'curStatus' &&
      target.parentNode.id != 'statusList' &&
      target.parentNode.id != 'fontsList' &&
      target.id != 'customMessage' &&
      target.parentNode.id != 'customMessage' &&
      target.id != 'emoticonList' &&
      target.className != 'emotIcon' &&
      target.id != 'fontSizeList' &&
      target.parentNode.id != 'fontSizeList' &&
      target.id != 'fontColorList' &&
      target.className != 'colorItem' &&
      target.className != 'tTable'
   ) // to put it simply, if you did not click on a list
   {
      Element.setStyle($('statusList'), {'display': 'none'});
      Element.setStyle($('emoticonList'), {'display': 'none'});
      Element.setStyle($('fontsList'), {'display': 'none'});
      Element.setStyle($('fontSizeList'), {'display': 'none'});
      Element.setStyle($('fontColorList'), {'display': 'none'});
      return;
   }
}

/**
 * Wait for a window (tag) to be destroyed then run
 * a function.
 *
 * @arguments
 *   id - window or tag that is being destroyed
 *   func - (function) run when window destroyed
 *
 * @author Daniel Howard
 **/
function waitForDestroy(id, func) {
   if ($(id)) {
      setTimeout(function() {
         if ($(id)) {
            setTimeout(this, 250);
         } else {
            func();
         }
      }, 250);
   } else {
      func();
   }
}

/**
 * Will check for the press of the 'Return'/'Enter' key,
 * if found, func() is ran. If it is not, then func2, if supplied,
 * and if it is a function, is ran.
 *
 * @arguments
 *   event - supplied by browser
 *   func - (function) run if Enter is pressed
 *   func2 - (function) run if Enter is not pressed
 *
 * @author Benjamin Hutchins
 **/
function handleInput(event, func, func2) {
   event = event || event.window;
   var asc = document.all ? event.keyCode : event.which;
   
   if (asc == 13) {
      func();
      return false;
   }

   if (typeof func2 == 'function')
      func2();

   return true;
}


/**
 * Will run through passed variable 'text' and fix
 * it's regExpression faults.
 *
 * @author Joshua Gross
 * @return fromatted 'text'
 **/
function regExpEscape(text) {
  if (!arguments.callee.sRE) {
    var specials = [
      '/', '.', '*', '+', '?', '|',
      '(', ')', '[', ']', '{', '}', '\\'
    ];
    arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
  }
  return text.replace(arguments.callee.sRE, '\\$1');
}


/**
 * Wrapper to scroll down within an element
 *
 * @arguments
 *   id - element to scroll down within
 *
 * @author Joshua Gross
 **/
function scrollToBottom(id) {
   $(id).scrollTop = $(id).scrollHeight - $(id).clientHeight;
}


/**
 * Strip out whitespace on then sides of 'text'
 *
 * @author Joshua Gross
 * @return trimmed 'text'
 **/
function trim(text) {
   if (text == null) return null;
   return text.replace(/^[ \t]+|[ \t]+$/g, "");
}


/**
 * Toggle audio on and off
 *
 * @author Joshua Gross
 **/
function toggleAudio() {
   if (audioNotify == true) {
      audioNotify = false;
      $('toggleaudio').src = 'themes/'+theme+'/window/audio_off.png';
   } else {
      audioNotify = true;
      $('toggleaudio').src = 'themes/'+theme+'/window/audio_on.png';
   }
}


/**
 * Make a window title and the web page "blink"
 *
 * @arguments
 *   room - room to show info in title bar
 *   message - message to show 'nlinking'
 *   alter - which step of blinking are we in
 *
 * @author Joshua Gross
 **/
function titlebarBlink(room, message, alter) {
   if (!titlebarBlinker) {
      document.title = defaultTitle;
      return;
   }
 
   if ((room.split('_', 2)[0] == 'im') && Chatroom.windows[room].detached) {
      Chatroom.windows[room].popup.titlebarBlink(room, message, alter);
      return;
   }
   
   if (alter == 0) {
      message = message.replace(/&#8220;/, '"');
      message = message.replace(/&#8221;/, '"');
      document.title = room + ': ' + message;
      alter = 1;
   } else if (alter == 1) {
      document.title = defaultTitle;
      alter = 0;
   }
  
   blinkerTimer = setTimeout("titlebarBlink('"+room+"', '"+message+"', "+alter+")", 1000);
}


/**
 * Toggle the variable 'titlebarBlinker' to true/false
 *
 * @author Joshua Gross
 * @update Benjamin Hutchins
 **/
function blinkerOn(onoff) {
   titlebarBlinker = (onoff == true ? true : false);
}


/**
 * Button effects for browsers without ":" support
 *
 * @arguments
 *   el - element to affect
 *
 * @author Joshua Gross
 **/
function buttonHover(el) {
   var newsrc = el.src;
   newsrc = newsrc.replace(/_hover/, '');
   el.src = newsrc.replace(/\.png/, '_hover.png');
}
function buttonDown(el) {
   el.src = el.src.replace(/_hover\.png/, '_down.png');
}
function buttonNormal(el) {
   el.src = el.src.replace(/\_hover.png/, '.png').replace(/\_down.png/, '.png');
}


/**
 * Check to see is an email is valid
 *
 * @arguments
 *   email - email to check
 *
 * @author Joshua Gross
 * @updated Benjamin Hutchins
 * @return true if email is valid, false otherwise
 **/
function checkEmailAddr(email) {
   var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
   return filter.test(email);
}

/**
 * Generates a random string
 *
 * @arguments
 *   length - length of string to be created
 *
 * @author Joshua Gross
 * @return random string
 **/
function randomString(length) {
   var chars = "abcdefghijklmnopqrstuvwxyz1234567890";
   var pass = "";
   var charLength = chars.length;

   for(x=0;x<length;x++) {
      i = Math.floor(Math.random() * charLength);
      pass += chars.charAt(i);
   }

   return pass;
}


/**
 * Gets the alias for a user
 *
 * @arguments
 *   user - user who has an alias
 *   room - room where user has an alias
 *
 * @author Daniel Howard
 * @return alias
 **/
function getAlias(user, room) {
  var aliasObj = $(user + '_' + room + '_chatUserAlias');
  return (aliasObj == null)? user: aliasObj.innerHTML;
}


/**
 * Gets the help description for a user
 *
 * @arguments
 *   alias - an alias determines a user's commands
 *
 * @author Daniel Howard
 * @return help message
 **/
function getHelpMessage(alias) {
   var message = 
      "/? or /help ... view this help<br />" +
      "<i>text</i> ... say something in character<br />" +
      "[<i>dice</i>] ... roll dice (e.g. [3d20], [d20], [3d4+2])<br />" +
      "/name <i>name</i> ... change your name<br />" +
      "/name GM ... change to GM mode<br />" +
      "/me <i>action</i> ... perform an action<br />" +
      "/ooc <i>text</i> ... say something out-of-character<br />" +
      "/whisper <i>user</i> <i>text</i> ... whisper to a user<br />" +
      "/share <i>link</i> <i>name</i> ... share a link<br />";

   if (alias == 'GM') {
      message =
         "/? or /help ... view this help<br />" +
         "<i>text</i> ... show text unchanged<br />" +
         "[<i>dice</i>] ... roll dice (e.g. [3d20], [d20], [3d4+2])<br />" +
         "/say <i>text</i> ... say something in character<br />" +
         "/name <i>name</i> ... change your name<br />" +
         "/me <i>action</i> ... perform an action<br />" +
         "/ooc <i>text</i> ... say something out-of-character<br />" +
         "/whisper <i>user</i> <i>text</i> ... whisper to a user<br />" +
         "/share <i>link</i> <i>name</i> ... share a link<br />";
   }
   var plugins_a = makeArray(plugins);
   for (var p=0; p < plugins_a.length; p++) {
      if ((typeof plugins_a[p].getHelpMessage) == 'function') {
         message += plugins_a[p].getHelpMessage(alias);
      }
   }
   return message;
}

/**
 * Find and replace a HTML tag.
 *
 * @arguments
 *   html - The HTML to search.
 *   regexp - RegExp object to find open tags.
 *   r - Function that transforms a tag's HTML.
 * @return The new HTML.
 * 
 * @author Daniel Howard
 **/
function replaceHtmlTag(html, regexp, r) {
   var newHtml = '';
   var match = regexp.exec(html);
   while (match != null) {
      var regexpEnd = new RegExp('<([\\S]+)[\\s]*', 'ig');
      var matchEnd = regexpEnd.exec(match[0]);
      regexpEnd = new RegExp('</'+matchEnd[1]+'>', 'ig');
      matchEnd = regexpEnd.exec(html.substring(match.index));
      if (matchEnd != null) {
         newHtml = html.substring(match.index, match.index+matchEnd.index+matchEnd[0].length);
         newHtml = r(newHtml, regexp, match);
         html = html.substring(0, match.index)+newHtml+html.substring(match.index+matchEnd.index+matchEnd[0].length);
         regexp.lastIndex += newHtml.length - (matchEnd.index+matchEnd[0].length);
      }
      match = regexp.exec(html);
   }
   return html;
}

/**
 * Find and modify a HTML tag.
 *
 * @arguments
 *   html - The source string to search.
 *   regexp - RegExp object to find tag(s).
 *   r - Tag handling function.
 * @return The new HTML.
 *
 * @author Daniel Howard
 **/
function modifyHtmlTag(html, regexp, r) {
   var clone = function(t) {
      var r = Object.clone(t);
      r.a = Object.clone(t.a);
      return r;
   };
   var tag = '';
   var regexpA = null;
   var matchA = null;
   var regexpW = null;
   var matchW = null;
   var p = '';
   var add = '';
   var mod = 0;
   var t = {};
   var tt = {};
   var ttt = {};
   var match = regexp.exec(html);
   while (match != null) {
      t = { name: null, body: null, a: {}, match: match, regexp: regexp };
      tag = match[0];
      regexpA = new RegExp('<([\\w]+)[\\s]*', 'ig');
      matchA = regexpA.exec(tag);
      t.name = matchA[1];
      if (!inArray(['input', 'img', 'br', 'hr'], t.name.toLowerCase())) {
         regexpA = new RegExp('</'+t.name+'>', 'ig');
         matchA = regexpA.exec(html.substring(match.index+match[0].length));
         if (matchA == null) {
            t.body = null;
         } else {
            t.body = html.substring(match.index+match[0].length, match.index+match[0].length+matchA.index);
         }
      }
      regexpA = new RegExp('[\\s]*([\\S]+)[\\s]*=[\\s]*("([^"]*?)"|\'([^\']*?)\'|(\\w+))', 'ig');
      matchA = regexpA.exec(tag);
      while (matchA != null) {
         t.a[matchA[1]] = matchA[3];
         matchA = regexpA.exec(tag);
      }
      tt = r(clone(t));
      if ((typeof tt) != 'undefined') {
         ttt = clone(t);
         for (p in tt.a) {
            ttt.a[p] = tt.a[p];
         }
         add = '';
         mod = 0;
         for (p in ttt.a) {
            if ((typeof t.a[p]) == 'undefined') {
               // add an attribute
               var value = tt.a[p];
               add += ' '+p+'="'+value+'"';
               mod += (' '+p+'="'+value+'"').length;
            } else if ((typeof tt.a[p]) == 'undefined') {
               // delete an attribute
               regexpA = new RegExp('[\\s]*'+p+'[\\s]*=[\\s]*("([^"]*?)"|\'([^\']*?)\'|(\\w+))', 'ig');
               matchA = regexpA.exec(tag);
               tag = tag.substring(0, matchA.index)+tag.substring(matchA.index+matchA[0].length);
               mod -= matchA[0].length;
            } else if (t.a[p] != tt.a[p]) {
               var value = tt.a[p];
               regexpA = new RegExp('[\\s]*'+p+'[\\s]*=[\\s]*("([^"]*?)"|\'([^\']*?)\'|(\\w+))', 'ig');
               matchA = regexpA.exec(tag);
               regexpW = new RegExp('[\\s]*'+p+'[\\s]*=[\\s]*["\']?', 'ig');
               matchW = regexpW.exec(tag);
               var ch = tag.substring(matchW[0].length-1, matchW[0].length);
               if (ch == '"' || ch == '\'') {
                  tag = tag.substring(0, matchW[0].length)+value+tag.substring(matchA.index+match[0].length-end);
               }
               var end = ((matchA[0].substring(matchA[0].length-1) == '\'') || (matchA[0].substring(matchA[0].length-1) == '"'))? 1: 0;
               tag = tag.substring(0, matchW.index+matchW[0].length)+value+tag.substring(matchA.index+matchA[0].length-end);
               mod += value.length - t.a[p].length;
            }
         }
         regexpW = new RegExp('(\\s|[/]?>)', 'i');
         matchW = regexpW.exec(tag);
         if (matchW != null) {
            tag = tag.substring(0, matchW.index)+add+tag.substring(matchW.index);
         }
         if (tt.body != null) {
            html = html.substring(0, match.index)+tag+tt.body+html.substring(match.index+match[0].length+t.body.length);
         } else {
            html = html.substring(0, match.index)+tag+html.substring(match.index+match[0].length);
         }
         
         regexp.lastIndex += mod;
         match = regexp.exec(html);
      } else {
         match = null;
      }
   }
   return html;
}

/**
 * Get indices in an array that meet a certain
 * condition.
 *
 * @arguments
 *   arr - array to be searched
 *   func - matching function
 *
 * @author Daniel Howard
 * @return An array of indices.
 **/
function meetsCondition(arr, func) {
   var idxs = [];
   for (var i=0; i < arr.length; ++i) {
      if (func(arr, i)) {
         idxs.push(i);
      }
   }
   return idxs;
}

/**
 * Return a subset of an array of values based on
 * an array of indices.
 *
 * @arguments
 *   arr - array of values
 *   arrIndices - array of indices
 *
 * @author Daniel Howard
 * @return A subset of the array of values.
 **/
function makeValueArray(arr, arrIndices) {
   var a = [];
   for (var i=0; i < arrIndices.length; i++) {
      a.push(arr[arrIndices[i]]);
   }
   return a;
}

/**
 * Convert an object (a hash) into an array.
 * 
 * Only values are saved; keys are thrown away.
 *
 * @arguments
 *   obj - object to be converted
 *
 * @author Daniel Howard
 * @return An array of values.
 **/
function makeArray(obj) {
   var a = [];
   for (var v in obj) {
      a.push(obj[v]);
   }
   return a;
}

/**
 * in_array for javascript
 *
 * @arguments
 *   arr - array to be searched
 *   value - item to search for
 *
 * @author Joshua Gross
 * @return true if 'value' is found in 'arr', false if it is not.
 **/
function inArray(arr, value) {
   var i;
   for (var group in arr) {
     // Matches identical (===), not just similar (==).
      for(i=0; i<arr[group].length; i++) {
         if (arr[group][i] === value)
            return true;
      }
   }
   return false;
};

Array.prototype.inArray = function(search_term) { // Adds inArray to all arrays
   var i = this.length;
   if (i > 0) {
      do {
         if (this[i] === search_term) {
            return true;
         }
      } while (i--);
   }
   return false;
};

/**
 * Return true if the end of a string matches the given
 * string.
 * 
 * @arguments
 *   s - the string to search
 *   end - the substring to test
 * @return True, if the string is at the end.
 *
 * @author Daniel Howard
 */
function endsWith(s, end) {
   return (end.length < s.length) && (s.indexOf(end, s.length - end.length) !== -1);
}

/**
 * Checks to see if a string is alphanumeric (only letters and numbers)
 *
 * @author Joshua Gross
 * @return true if string is alphanumeric, false otherwise
 **/
String.prototype.isAlphaNumeric = function() {return /^[A-Za-z0-9_\d]+$/.test (this);};

/**
 * Invoke a callback several times or until 'false' is returned.
 * 
 * The first call is immediate.
 * 
 * @arguments
 *   callback - a callback to invoke repeatedly
 *   interval - time in milliseconds between calls
 *   times - the maximum number of times to call
 */
function setIntervalCheck(callback, interval, times) {
   var callbackBoolean = function() {
      var ret = callback();
      return ((typeof ret == "undefined") || ret);
   };
   var intervalCallback = function(t) {
      return function() {
         if (--t >= 0) {
            if (callbackBoolean()) {
               window.setTimeout(intervalCallback, interval);
            }
         }
      };
   }(times-1);
   if (callbackBoolean()) {
      window.setTimeout(intervalCallback, interval);
   }
}

/**
 * Load the theme stylesheet
 **/
var loadCSS = document.createElement("link");
loadCSS.setAttribute("rel", "stylesheet");
loadCSS.setAttribute("type", "text/css");
loadCSS.setAttribute("href", 'themes/' + theme + '/style.css');
if (typeof loadCSS != "undefined") {
   document.getElementsByTagName("head")[0].appendChild(loadCSS);
}

