/* shared initialization
------------------------------------------------------- */
var WR={};
var global = {};
var config = {};


WR.userInfo = function(accountId) {
  global.infoAccountId = accountId;
  new Modlog.construct(ViewAccount.infoInlay).open();
};

WR.sendMessageTo = function(recipientId, recipientNickname, recipientAvatar) {
  global.recipientId=recipientId;
  global.recipientNickname=recipientNickname;
  global.recipientAvatar=recipientAvatar;
  new Modlog.construct(ViewMailbox.messageInlay).open();
};

WR.windowReload = function() {
  window.location.reload();
};

WR.openURI = function(uri) {
  uri = uri.trim();
  if (uri === 'http://') {
      return ;
  }

  if (uri.substring(0, 'http://'.length) != 'http://' &&
      uri.substring(0, 'https://'.length) != 'https://') {
      uri = 'http://' + uri;
  }

  top.location.href = config.SITE_URL + 'discuss/?uri=' + encodeURIComponent(uri);
};

WR.pasteUri = function(jqObj) {
  if(!window.clipboardData || !clipboardData.getData) 
      return ;

  var uri = clipboardData.getData("Text");
  if (uri.indexOf('http://') === 0)
      jqObj.val(uri);
};


/* Extending JS-Objects
------------------------------------------------------- */

// http://groups.google.de/group/de.comp.lang.javascript/browse_thread/thread/ab7f9b8f967c3193/48c51843bfa3d723?lnk=st&q=javascript+array+contains&rnum=4&hl=de#48c51843bfa3d723
Array.prototype.contains = function(searchValue) {
  for (var i = 0, len = this.length; i < len && this[i] !== searchValue; i++) ;
  return i < len;
};

// http://www.ditchnet.org/wp/2005/04/04/enhance-your-javascript-arrays/
Array.prototype.removeAt = function (iIndex) {
  return this.splice(iIndex, 1);
};

Array.prototype.remove = function(element) {
  var result = 0;
  for (var i = 0; i < this.length; ) {
    if (this[i] == element) {
      this.removeAt(i);
      ++result;
    }
    else {
      i++
    }
  }
  return result;
};

String.prototype.trim = function() {
  return this.replace(/^\s*([\s\S]*\S+)\s*$|^\s*$/, '$1');
};

String.prototype.nl2br = function() {
  return this.replace(/\r\n|\r|\n/g, '<br \/>');
};

// from prototype...
String.prototype.stripTags = function() {
  return this.replace(/<\/?[^>]+>/gi, '');
};

String.prototype.startsWith = function(prefix, offset) {
  var offset = offset || 0;
  if(offset < 0 || offset > this.length) return false;
  return this.substring(offset, offset + prefix.length) == prefix;
};

String.prototype.endsWith = function(suffix) {
  return this.substring(this.length - suffix.length) == suffix;
};

// from prototype...
String.prototype.escapeHTML = function() {
  var div = document.createElement('div');
  var text = document.createTextNode(this);
  div.appendChild(text);
  return div.innerHTML;
};

// from http://www.crockford.com/javascript/remedial.html
// param = {domain: 'valvion.com', media: 'http://media.{domain}/'};
// url = "{media}logo.gif".supplant(param);

String.prototype.supplant = function (o) {
  var i, j, s = this, v;
  for (;;) {
    i = s.lastIndexOf('{');
    if (i < 0) {
      break;
    }
    j = s.indexOf('}', i);
    if (i + 1 >= j) {
      break;
    }
    v = o[s.substring(i + 1, j)];
    if (!isString(v) && !isNumber(v)) {
      break;
    }
    s = s.substring(0, i) + v + s.substring(j + 1);
  }
  return s;
};

String.prototype.ucfirst = function () {
   return this.substr(0,1).toUpperCase() + this.substr(1, this.length);
};

/* visual effects
------------------------------------------------------- */

// credits to Peter-Paul Koch, http://www.evolt.org/article/document_body_doctype_switching_and_more/17/30655/
function posTop() {
  if (window.pageYOffset)
    return window.pageYOffset;
  else if (document.documentElement && document.documentElement.scrollTop)
    return document.documentElement.scrollTop;
  else if (document.body)
    return document.body.scrollTop;
};

function charsRemaining(id, maxLength) {
  var inputField = document.getElementById(id);
  if(inputField.value.length <= maxLength)
    return ;
  
  inputField.value = inputField.value.substr(0, maxLength);
};


function scrollToTop() {
  $('body').ScrollTo(500);
};

function inputFocus(inputObj, text, color) {
  if (color) inputObj.style.color = color;
  if (inputObj.value === text) inputObj.value = '';
};

function inputBlur(inputObj, text, color) {
  if (inputObj.value === '' || inputObj.value === text) {
    inputObj.value = text;

    if (color) {
      inputObj.style.color = color;
    }
    return ;
  }
};

/* AJAX / XmlHelper
------------------------------------------------------- */


$(document).ajaxStart(function(){ Debug.log('ajaxStart');$('#loading').show();}); 
$(document).ajaxStop(function(){ Debug.log('ajaxStop');$('#loading').hide();}); 
$.ajaxTimeout(15000);

function XmlHelper() {
  // http://w3future.com/html/stories/hop.xml (Using methods as functions).
  var me = this;

  this.send = function(params, onload) {
    me.onload  = onload;
    
    // check if requested package/method needs login
    if (!global.accountId && 
        params.p && 
        params.m && 
        global.loginRequiringMethods[params.p] && 
        global.loginRequiringMethods[params.p].contains(params.m)) {
      new Modlog.construct(ViewAccount.loginInlay,{'errorText': 'Please login to do this.'}).open();
      return ;
    }
    
    data = [];
    $.each(params, function(key){data.push({name:key,value:this});});

    $.ajax({
      url:      config.API_URL,
      type:     'POST',
      data:     $.param(data),
      dataType: 'xml',
      //ifModified: 1,

      complete: me.ajaxComplete,
      success:  me.ajaxSuccess,
      error:    me.ajaxError
    });
  };
  
  this.formSubmit = function(form, callback, preProcessParamsCallback) {
    // initial state
    $(form).find('input[@type=text]').removeClass().addClass('std');
    $(form).find('input[@type=password]').removeClass().addClass('std');
    $(form).find('textarea').removeClass().addClass('std');
    $(form).find('div.hint-error').removeClass().addClass('hint');
    
    // params
    var paramsRaw = $(form).serialize().vars;
    var params = {};
    for (var i = 0; i < paramsRaw.length; i++) {
      params[paramsRaw[i].name] = paramsRaw[i].value;
    }
    if (preProcessParamsCallback)
      params = preProcessParamsCallback(params);
    
    // send
    xh.send(params, callback);
    return false;
  };
  
  this.ajaxSuccess = function(xml) {
    Debug.log('ajaxSuccess');
    statusCode     = null;
    subStatusCodes = null;

    var accountId = (xml.documentElement.getAttribute('accountId') !== null)
      ? xml.documentElement.getAttribute('accountId')
      : false;

    // do not do that for login, since the wrsess cookie will be possibly overridden
    // from listeners. only logout should be handled this way...
    if (global.accountId && !accountId) {
      global.accountId = false;
      loginState.notify();
    }

    if (xml.getElementsByTagName("status")[0]) {
      statusCode = xml.getElementsByTagName("status")[0].getAttribute('code');

      if (statusCode == 'FAIL') {
        var subStatusCodes = [];
        var childNodes = xml.getElementsByTagName("status")[0].childNodes;

        for(var i = 0; i < childNodes.length; i++) {
          subStatusCodes.push(childNodes[i].getAttribute('code'));
        }

        if (subStatusCodes.contains('API_UNEXPECTED_FATAL') || subStatusCodes.contains('METHOD_UNKNOWN')) {
          new Modlog.construct(ViewBase.notifyInlay, {
            'title': 'Unexpected error occured',
            'text' : 'Ooops, some piece of ' + config.APP_NAME + ' was lousy programmed. The ' + config.APP_NAME + ' team has been reproved automatically and is working hard to fix the error. Sorry for the unconvenience!',
            'btn'  : 'Close',
            'alert': true
          }).open();
          return ;
        }

        if (subStatusCodes.contains('API_ACCESS_BLOCKED')) {
          new Modlog.construct(ViewBase.notifyInlay, {
            'title': 'Access blocked',
            'text' : 'Access to ' + config.APP_DOMAIN + ' has been blocked. Check our <a href="' + config.SITE_URL + '">Homepage</a> for further information.',
            'btn'  : 'Close',
            'alert': true
          }).open();
          return ;
        }

        if (subStatusCodes.contains('AUTHORIZATION_FAILED')) {
          new Modlog.construct(ViewBase.notifyInlay, {
            'title': 'Authorization failed',
            'text' : 'Sorry, you are not allowed to do this.',
            'btn'  : 'Close',
            'alert': true
          }).open();
          return ;
        }

        if (subStatusCodes.contains('LOGIN_REQUIRED')) {
          if (global.accountId) {
            global.accountId = false;
            loginState.notify();
          }
          new Modlog.construct(ViewAccount.loginInlay,{'errorText': 'Please login to do this.'}).open();
          return ;
        }

        if (subStatusCodes.contains('URI_NOT_VALID')) {
          new Modlog.construct(ViewBase.notifyInlay, {
            'title': 'Invalid web page',
            'text' : 'The web page address you are trying to comment on is invalid. Valid web page adresses start with either <em>http://</em> or <em>https://</em>.<br /><br />Please note that it is furthermore not possible to discuss Webride discussions (currently, at least!).',
            'btn'  : 'Close',
            'alert': true
          }).open();
          return ;
        }
      }
    }

    // proceed with callback handler. handles success AND fail
    me.onload(xml, statusCode, subStatusCodes);  
  };

  this.ajaxComplete = function(xmlhttp, JQueryStatus) {
    Debug.log('ajaxComplete:' + JQueryStatus);
  };

  this.ajaxError = function(xmlhttp, JQueryStatus) {
    Debug.log('ajaxError:' + JQueryStatus);
  
    $('#loading').hide();
    new Modlog.construct(ViewBase.notifyInlay, {
      'title': config.APP_DOMAIN + ' unreachable',
      'text' : config.APP_DOMAIN + ' seems to be out of reach right now. Maybe your network is down, or the ' + config.APP_NAME + ' server might be slowed down by  unexpectedly high traffic.<br /><br />So get yourself a tasty cup of coffee and try again later.',
      'btn'  : 'Close',
      'alert': true
    }).open();
  };
};

$(document).ready(function(){
  xh = new XmlHelper(); // global XmlHelper 
});

jQuery.fn.wrSubmit = function(callback, preProcessParamsCallback) {
  $(this).get(0).setAttribute('accept-charset', 'utf-8');
  $(this).get(0).setAttribute('method', 'post');
  $(this).submit(function(){
    return xh.formSubmit(this, callback, preProcessParamsCallback);
  });
};


/* Modlog (MODal diaLOG) class
------------------------------------------------------- */

var modlogStack = [];
var Modlog = {};

Modlog.CLOSE_MODE_OK     = 0;
Modlog.CLOSE_MODE_CANCEL = 1; // this forces onbeforecancel to be checked

Modlog.onkeypress = function(e) {
  e = e || window.event;
  if (e.keyCode == 27) {
    var closed = Modlog.close(Modlog.CLOSE_MODE_CANCEL); // closed == true indicates that a modlog was closed

    // stop progagation of key event, closing
    // multiple modlogs with one key press
    if (window.ActiveXObject)
      window.event.cancelBubble = true;

    if (e.stopPropagation)
      e.stopPropagation();
  }
};


// always closes the last opened modlog
Modlog.close = function(mode) {
  if (modlogStack.length == 0)
    return false;

  var modlogObj = modlogStack[modlogStack.length - 1];
  if (modlogObj.coObj.doScrollToTop())
    scrollToTop();

  modlogObj.close(mode);

  return true; // indicates that there might be further open modlogs
};

Modlog.replaceContent = function(contentObj, coParams, callback) {
  if (modlogStack.length != 0) {
    Modlog.close(Modlog.CLOSE_MODE_OK);
  }

  new Modlog.construct(contentObj, coParams, callback).open();
};

// only one purpose: redraw the cover so the
// complete screen is always locked for user
// interaction. if we do not do this, the
// locked area will remain static while we
// may maximize the window and the clickable
// parts, underlying the cover

Modlog.resize = function() {
  if (modlogStack.length == 0)
    return false;
  
  var ih = getWindowInnerHeight();
  $('.cover').css({
    'width': '100%',
    'height': ih + 'px'
  });
};

Modlog.changeState = function(modlogObjGuid, state) {
  switch (state) {
    case 'neutral':
      Modlog.color(modlogObjGuid, 'neutral');
      break;
    case 'failure':
      Modlog.color(modlogObjGuid, 'failure');
      break;
    case 'success':
      Modlog.color(modlogObjGuid, 'success');
      $('#modlog_' + modlogObjGuid + '.mlBody').cover(1);
      break;
  }
};


Modlog.color = function(modlogObjGuid, type) {
  switch (type) {
    case 'neutral': 
      $('#modlog_' + modlogObjGuid).css({'background':'url(/shared/img/modlogCover.png)'});
      $('#modlog_' + modlogObjGuid + ' .mlInner').css({'borderColor':'#000'});
      $('#modlog_' + modlogObjGuid + ' .mlHeader').css({'background':'#000'});
      break;
    case 'failure': 
      $('#modlog_' + modlogObjGuid).css({'background':'url(/shared/img/modlogCoverFailure.png)'});
      $('#modlog_' + modlogObjGuid + ' .mlInner').css({'borderColor':'#c03'});
      $('#modlog_' + modlogObjGuid + ' .mlHeader').css({'background':'#c03'});
      break;
    case 'success': 
      $('#modlog_' + modlogObjGuid).css({'background':'url(/shared/img/modlogCoverSuccess.png)'});
      $('#modlog_' + modlogObjGuid + ' .mlInner').css({'borderColor':'#060'});
      $('#modlog_' + modlogObjGuid + ' .mlHeader').css({'background':'#060'});
      break;
  }
};


Modlog.construct = function(contentObj, coParams, callback) {
  if (contentObj == null) {
    contentObj = ViewBase.notifyInlay;
  }

  // test coObj interface
  if (!contentObj.guid) alert('Modlog.construct: contentObj interface not fully implemented');

  // only one modlog, specified by guid is allowed to open up
  if (typeof this.getDivByModlogGuid(contentObj.guid) == 'object')
    return false;

  this.coObj = contentObj;
  this.isModal = (typeof this.coObj.modal == 'undefined') || this.coObj.modal == true; // set this parameter in the definition of the contentObject
  this.coParams = coParams;
  this.callback = callback;

  if (this.coObj.init && !this.coObj.init())
    return false; // cancelled open
};

Modlog.construct.prototype = {

  open: function() {
    if (this.isModal) {
      var modlogZIndex = 2 * (modlogStack.length + 1) + 1000;
      $('.cover').css({
        'zIndex': modlogZIndex,
        'width': '100%',
        'display': 'block'
      });

      var ih = getWindowInnerHeight();
      $('.cover').css({'height':ih + 'px'});
    }

    if (modlogStack.length == 0) { // we are preparing a modlog to show up, so we must make our modlogs-container visible
      $('#modlogs').show();
    }

    this.createDiv();
    $('#modlog_' + this.coObj.guid).css({'zIndex': 2*(modlogStack.length+1)+1001});
    $('#modlog_' + this.coObj.guid).Draggable({ // http://www.eyecon.ro/interface/
        handle:'.mlTitle',
        zIndex:modlogZIndex,
        containment:'document'
    });


    if (this.coObj.maxWidth)
      $('#modlog_' + this.coObj.guid).css({'max-width':this.coObj.maxWidth});

    if (this.coObj.doScrollToTop())
      scrollToTop();

    var modlogObj = this;
    modlogStack.push(modlogObj);

    // show modlog
    $('#modlog_' + this.coObj.guid).show();

    if (this.coObj.url) {
      $('#mlBody_' + this.coObj.guid).cover('Loading...');
      var innerThis = {
        coObj:this.coObj,
        coParams:this.coParams?this.coParams:null,
        callback:this.callback
      };
      //log(innerThis);
      
      $('#mlBody_' + this.coObj.guid).load(this.coObj.url + '?v=' + config.v, innerThis, function(){
        $(this).uncover();
        if (!innerThis.coObj.onopen)
          return ;
          
        if (innerThis.coParams) {
          innerThis.coObj.onopen(innerThis.coParams,innerThis.callback); // ViewBase.notifyInlay, ViewAccount.loginInlay
        } else {
          innerThis.coObj.onopen(innerThis.callback);
        }
      });
    }
    else {
      if (this.coParams)
        this.coObj.onopen(this.coParams,this.callback); // ViewBase.notifyInlay
      else
        this.coObj.onopen(this.callback);
    }
  },

  close: function(mode) {
    if (mode == Modlog.CLOSE_MODE_CANCEL) {
      if (this.coObj.onbeforeclose && !this.coObj.onbeforeclose())
        return false;
    }

    this.deleteDiv();

    if (modlogStack.length == 0) {
      $('#modlogs').hide();
    }

    // delete from stack
    modlogStack.pop();

    if (this.isModal) {
      if (modlogStack.length == 0) {
        // css-variant. makes cursor invisble :(
        $('.cover').css({
          width:0,
          height:0,
          zIndex:0
        });
      }
      else {
        $('.cover').css('zIndex', 2 * modlogStack.length + 1000);
      }
    }

    if (this.coObj.onclose)
      this.coObj.onclose();
  },

  // creates a new modlog-div, appends it to the modlogs-div
  createDiv: function() {
    var modlog = document.createElement('div');
    modlog.id = 'modlog_' + this.coObj.guid;
    modlog.className = 'modlog';

    modlog.style.top = this.coObj.doScrollToTop()
      ? (40 + (modlogStack.length - 1 ) * 10) + 'px'
      : (20 + posTop() + (modlogStack.length - 1 ) * 10) + 'px';

    if (this.coObj.width) modlog.style.width = this.coObj.width;

    modlog.style.left = (6 + (modlogStack.length - 1 ) * 2) + '%';
    $('#modlogs').get(0).appendChild(modlog);

    $('#' + modlog.id).html([
      '<div class="mlInner">',
        '<div class="cf mlHeader">',
          '<table>',
          '<tr>',
            '<td class="mlTitle" id="mlTitle_' + this.coObj.guid + '"></td>',
            (this.coObj.hideClose ? '' : '<td class="mlClose"><a href="javascript:;" onclick="Modlog.close(Modlog.CLOSE_MODE_CANCEL)" title="Tipp: press &lt;ESC&gt; on your keyboard">close</a></td>'),
          '</tr>',
          '</table>',
        '</div>',
        '<div id="mlBody_' + this.coObj.guid + '" class="cf mlBody">&nbsp;</div>',
      '</div>'
      ].join('')
    );
  },

  // deletes the modlog div, created previously
  deleteDiv: function() {
    var node = $('#modlogs').get(0);
    node.removeChild(this.getDivByModlogGuid(this.coObj.guid));
  },

  getDivByModlogGuid: function(modlogGuid) {
    var node = $('#modlogs').get(0);
    var childNodes = node.childNodes;
    for(i = 0; i < childNodes.length; i++) {
      if (childNodes[i].id == 'modlog_' + modlogGuid) {
        return childNodes[i];
      }
    }
  }
};


$(window).resize(Modlog.resize); // adjust cover
$(document).keypress(Modlog.onkeypress); //ESC


/* Cookie handling
------------------------------------------------------- */

// credits to http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
};

function readCookie(name){
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++){
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
};

function eraseCookie(name) {
  createCookie(name,"",-1);
};

// reads querystringified (a=1&b=2&c=...) value from
// cookie and adds/replaces a key/value pair
function setCookieListItem(name, key, value) {
  c = readCookie(name);
  if (c === null) {
    var cookieVal = key + '=' + value;
    createCookie(name,cookieVal,365);
  }
  else {
    var cookieValArr = [];
    var pair = c.split('&');
    var set = 0;
    for (i = 0; i < pair.length; i++) {
      if (pair[i].split('=')[0] == key) {
        set = 1;
        cookieValArr.push(pair[i].split('=')[0] + '=' + value);
      }
      else {
        cookieValArr.push(pair[i].split('=')[0] + '=' + pair[i].split('=')[1]);
      }
    }
    if (set == 0) {
      cookieValArr.push(key + '=' + value);
    }

    cookieVal = cookieValArr.join('&');
  }
  createCookie(name,cookieVal,365);
};

// turns a querystringified cookie value and
// turns it to an array
function getCookieListAsObj(name) {
  c = readCookie(name);
  if (c === null)
    return null;

  var obj={};
  var options=c.split('&');
  for (i = 0; i < options.length; i++) {
    obj[options[i].split('=')[0]] = options[i].split('=')[1];
  }
  return obj;
};




/* observables
------------------------------------------------------- */

//-- class to inherit from/merge with --//

var Observable = {};

Observable.create = function() {
  // http://w3future.com/html/stories/hop.xml (Using methods as functions).
  // we need this, since we may pass the observale as a callback
  me = this; // "self" seems to be in use, at least in IE
}; // constructor

Observable.create.prototype = {
  value:     null,
  listeners: [],

  register: function(id, callback) {
    for (var i = 0; i < me.listeners.length; i++) {
      if (me.listeners[i].id == id)
          return false;
    }
    
    me.listeners.push({id:id, callback:callback});
  },

  unregister: function(id) {
    for (var i = 0; i < me.listeners.length; i++) {
      if (me.listeners[i].id == id) {
        me.listeners.splice(i, 1);
      }
    }
  },

  notify: function() {
    if (me.listeners.length == 0)
      return false;

    for (var i = 0; i < me.listeners.length; i++) {
      Debug.log('notify(), calling ' + me.listeners[i].id);
      me.listeners[i].callback.call();
    }
  }
};



/* debug dialog
------------------------------------------------------- */

var Debug = {
  logId: 0,

  log: function(text) {
    if (!debug) return ;

    // put it in try/catch, since console.log has problems with unicode
    try {
      console.log(text);
    }
    catch (e) {
      // check if the debug-window is there
      if (!$('#debug-output').get(0))
        return ;

      var child = document.createElement('div');
      child.style.backgroundColor = Debug.logId % 2 == 0 ? '#eee' : '#fff';
      child.style.padding = '5px';
      var textNode = document.createTextNode('#' + (Debug.logId++) + ' ' + text);
      child.appendChild(textNode);

      if ($('#debug-output').get(0).childNodes.length) {
        var firstChild = $('#debug-output').get(0).childNodes[0];
        $('#debug-output').get(0).insertBefore(child, firstChild);
      }
      else {
        $('#debug-output').get(0).appendChild(child);
      }
    }
  },

  openDebugDialog: function () {
    if (!debug) return ;
    try { if (console) return ;} catch (e) { return ; } // strange behavior of IE, when trying to test "console"

    var debugDiv = document.createElement('div');
    debugDiv.id = 'debug';
    var style = 'font-size:11px;height:370px;padding:10px;width:700px;background-color:#fee;border:2px solid #c00;bottom:50px;right:10px;display:none;z-index:2000;';

    if (document.all) {
      debugDiv.setAttribute('style', style + 'position:absolute;top:expression(eval(document.body.scrollTop + 400));');
    } else {
      debugDiv.setAttribute('style', style + 'position:fixed;');
    }

    debugDiv.innerHTML =
      '<div id="debug-toolbar" style="margin-bottom:10px">' +
          '<button id="debug-clear">clear</button> ' +
          '<button id="debug-play">stop</button> ' +
          '<button id="debug-close">close</button>' +
      '</div>' +
      '<div id="debug-output" style="border:2px solid #c00;padding:0px;overflow:auto;height:310px;background-color:#fff;"></div>';

    document.body.appendChild(debugDiv);
    $('#debug-clear').click(function() {
      $('#debug-output>div').remove();
    });

    $('#debug-play').click(function() {
      if (debug) $('#debug-play').html('start');
      else       $('#debug-play').html('stop');
      debug = !debug;
    });

    $('#debug-close').click(function() {
      debug = false;
      $('#debug').hide();
    });

    $('#debug').show();
  }
};

log = Debug.log;

/* visual effects
------------------------------------------------------- */
/*
jQuery.fn.blink(className, repeatCounter) = function {
  repeatCounter = repeatCounter || 5;
  $(this).toggleClass(className);
  if (repeatCounter > 0) {
    bb = window.setTimeout("$(this).blink('" + className + "', " + (repeatCounter - 1) + ")", 150);
  }
  else {
    window.clearTimeout(bb);
  }
}
*/


/* jquery extensions
-------------------------------------------------------- */

// ...

/* uncategorized
-------------------------------------------------------- */

/* parse querystring and save params in "urlParams"
------------------------------------------------------- */
function setUrlParams() {
  var urlParams = {};
  var query = location.search.substring(1);
  var pairs = query.split("&");

  for (var i = 0; i < pairs.length; i++) {
    var pos = pairs[i].indexOf('=');
    if (pos == -1) continue;
    var argname = pairs[i].substring(0,pos);
    var value = pairs[i].substring(pos+1);
    urlParams[argname] = unescape(value);
  }

  global.urlParams = urlParams;
};


// post fetch gravatars
function loadGravatars() {
  $('img.gravatar').each(function() {
    var gravatarSize = this.width;
    //if (document.all) gravatarSize = gravatarSize; // hack - ie adds 4 pixels. fix this with something nicer

    var gravatarId = this.id.split('_')[1];
    if (gravatarId && gravatarId.length == 32) {
      this.src = 'http://www.gravatar.com/avatar.php?gravatar_id=' + gravatarId + '&size=' + gravatarSize + ';border=FF0000&rating=PG&default=' + config.SITE_URL + 'shared/img/gravatar_na_' + gravatarSize + 'x' + gravatarSize + '.gif';
    }
  });
};

// credits to Christian Kruse: http://aktuell.de.selfhtml.org/tippstricks/programmiertechnik/email/
function isValidEmail(email) {
  var proto  = "(mailto:)?";
  var usr    = "([a-zA-Z0-9][a-zA-Z0-9_.-]*|\"([^\\\\\x80-\xff\015\012\"]|\\\\[^\x80-\xff])+\")";
  var domain = "([a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,5}";
  var regex  = "^" + proto + "?" + usr + "\@" + domain + "$";

  var rgx    = new RegExp(regex);
  return rgx.exec(email) ? true : false;
};

function isValidURL_loose(url) {
  return (url.indexOf('http://') == 0 && url.length > 'http://'.length);
};

function drawHeaderRight() {
  // keep in sync with server!
  if (global.accountId) {
    html = '<div id="mailbox-href"><a href="#" onclick="new Modlog.construct(ViewMailbox.mailboxInlay).open();"><img src="/shared/img/mailbox_small_' + (global.unreadMessages == 1 ? 'on' : 'off') + '.gif" alt="My mailbox" /></a></div>' +
           '<div><a href="' + config.SITE_URL + 'users/' + global.nickname + '/" title="Logged in as &quot;' + global.nickname + '&quot;">My profile</a></div>' +
           '<div><a href="javascript:;" class="inline" onclick="this.blur();ApiAccount.logout()">Logout</a></div>';
  }
  else {
    html = '<div id="signup-href"><a href="javascript:;" class="inline" onclick="this.blur();new Modlog.construct(ViewAccount.signupInlay).open()">Signup</a></div>' +
           '<div><a href="javascript:;" class="inline" onclick="this.blur();new Modlog.construct(ViewAccount.loginInlay).open()">Login</a></div>';
  }

  //html+= '<div class="button" style="margin-right:0"><div class="button-inner"><a href="javascript:;" onclick="this.blur();new Modlog.construct(ViewSearch.searchInlay).open()" title="Search Webride.org">Search</a></div></div>';
  $('#hr').html(html);
};

function getWindowInnerHeight() {
  // this one seems to work better than the thickbox implementation (20060908)
  if ('undefined' != typeof document.documentElement && document.documentElement.clientHeight > document.body.scrollHeight)
    return parseInt(document.documentElement.clientHeight,10);

  return parseInt(document.body.scrollHeight,10);
}

function doFocus() {
  if (typeof global.focusFieldId == 'undefined')
    return ;

  $('#' + global.focusFieldId).get(0).focus();
};

function delayedFocus(id) {
  // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=236791
  window.setTimeout('if ($("#' + id + '").get(0)) $("#' + id + '").get(0).focus();', 500);
};

// borrowed (and modified) from prototype
function escapeHTML(s) {
  var div = document.createElement('div');
  var text = document.createTextNode(s);
  div.appendChild(text);
  return div.innerHTML;
};

function setLang(img, lang) {
  createCookie('lang',lang,365);
  WR.windowReload();
};

function DOMNodeCreate(nodeName, html, attribs) {
  var node = document.createElement(nodeName);
  if (html)
    node.innerHTML = html;

  for (a in attribs)
    node.setAttribute(a, attribs[a]);

  return node;
};

jQuery.fn.blink=function(duration,times,callback){
  return this.each(function(){
    me = this;
    times = times * 2;
  
    blinkOn = function(){
      $(me).addClass('blink');
      
      if (--times > 0)
        setTimeout(blinkOff, duration);
      else
        if (callback) callback();
    };
    blinkOff = function(){
      $(me).removeClass('blink');
      
      if (--times > 0)
        setTimeout(blinkOn, duration);
      else
        if (callback) callback();        
    };
  
    blinkOn();
  });
};


function doAdjustIFrameSize (iframeWindow) {
  if (iframeWindow.document.height) {
    var iframeElement = document.getElementById(iframeWindow.name);
    iframeElement.style.height = iframeWindow.document.height + 'px';
    iframeElement.style.width = iframeWindow.document.width + 'px';
  }
  else if (document.all) {
    var iframeElement = document.all[iframeWindow.name];
    if (iframeWindow.document.compatMode && iframeWindow.document.compatMode != 'BackCompat') {
      iframeElement.style.height = iframeWindow.document.documentElement.scrollHeight + 5 + 'px';
      iframeElement.style.width  = iframeWindow.document.documentElement.scrollWidth + 5 + 'px';
    }
    else {
      iframeElement.style.height = iframeWindow.document.body.scrollHeight + 5 + 'px';
      iframeElement.style.width = iframeWindow.document.body.scrollWidth + 5 + 'px';
    }
  }
};

function initInlayTabs() {
  var initCallback = $('.inlay-tabs').get(0).getAttribute('name');
  if (initCallback != null) 
    eval(initCallback + '()');
  
  $('.inlay-tabs div')
    .click(function(){
      $('.inlay-tabs div').removeClass('selected');
      $('.inlay-wrapper div.content').hide();
      var tabId = $(this).get(0).getAttribute('name');
      
      var callback = $('#content-' + tabId).get(0).getAttribute('name');
      if (callback != null)
        eval(callback + '()');
      
      $('#content-' + tabId).show();
      $(this).addClass('selected');
    })
    .hover(
      function(){$(this).addClass('hover')},
      function(){$(this).removeClass('hover')}
    );
};

jQuery.fn.cover = function(text) {
  return this.each(function() {
    //debugger;
    jq = $(this);
    jq.css({'position':'relative'});

    // get current z-index and add 1 to cover the actual element
    var z = parseInt(jq.css('zIndex')) + 1;
    var z = isNaN(z)?1:z;

    jq.prepend('<div class="cover" style="z-index:'+z+';display:block"></div>');
    if (text) jq.prepend('<div class="coverText" style="z-index:'+(z+1)+';display:block"><span class="loading">' + text + '</span></div>');
  });
};

jQuery.fn.uncover = function() {
  return this.each(function(){
    $(this).css({'position':'static'});
    $(this).find('div.coverText').remove().end().find('div.cover').remove().end();
  });
};


var loadScriptCache = [];
jQuery.fn.loadScript = function(src) {
  if (loadScriptCache.contains(src)) {
    // remove already loaded script from document
    $(this,document).find('script[@src="' + src + '"]').remove();
  }
  else
    loadScriptCache.push(src);
    
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = src;
  
  Debug.log('loadScript: ' + src);
  $(this,document).append(script);
};

var loadStyleCache = [];
jQuery.fn.loadStyle = function(href) {
  if (loadStyleCache.contains(href)) {
    // css files won't change and won't execute any dynamic code
    return ;
  }

  loadStyleCache.push(href);
    
  var script = document.createElement('link');
  script.type = 'text/css';
  script.rel  = 'stylesheet';
  script.href = href;
  
  Debug.log('loadStyle: ' + href);
  $(this,document).append(script);
};


// reloads image src
jQuery.fn.reload = function() {
  return this.each(function(){
    $(this).get(0).src = $(this).get(0).src + '?' + (new Date()).getTime();
  });
};

// cuts a text short and adds an expand/shrink link to it
jQuery.fn.cutShort = function(maxLength, moreCaption, lessCaption) {
  return this.each(function(){

    var text = $(this).html();
    if (text.length <= maxLength) 
      return this; // nothing to do

    if (typeof moreCaption == 'undefined') moreCaption = 'more »';;
    if (typeof lessCaption == 'undefined') lessCaption = '« less';;
  
    var visText = text.substring(0,maxLength);
    var hidText = text.substring(maxLength);

    var uniqid = Math.random() * Math.pow(10,17) + Math.random() * Math.pow(10,17)
               + Math.random() * Math.pow(10,17) + Math.random() * Math.pow(10,17);
    
    cutShort = // csH=Hidden,csT=Toggle
      visText 
      + '<span id="csH'+uniqid+'">' + hidText + '</span>'
      + ' <a class="inline" id="csT'+uniqid+'" href="javascript:;">' + moreCaption + '</a>';


    $(this).html(cutShort);
    $('#csH'+uniqid).css({'display':'none'});
    $('#csT'+uniqid).css({'whiteSpace':'nowrap'});
    $('#csT'+uniqid).click(function(){
      this.blur();
      var caption=($(this).html()==lessCaption)?moreCaption:lessCaption;
      $(this).html(caption);
      $('#csH'+uniqid).toggle();
    });
      
    return this;
  });
};

/**
 * taken and modified from http://jquery.com/dev/svn/plugins/form/form.js
 *

 * This function gathers form element variables into an array that
 * is embedded into the current "this" variable as "this.vars". It
 * is normally used in conjunction with formdata() or ajaxSubmit() but can
 * be used standalone as long as you don't need the x and y coordinates
 * associated with an <input type="image"/> element..
 *
 * Standalone usage examples:
 *
 * 1. Gather form vars and return array to LHS variable.
 *    var myform = $('#form-id').serialize();
 *
 * 2. Provide a serialized URL-ready string (after 1. above).
 *    var mystring = $.param(myform.vars);
 *
 * 3. Gather form vars and send to RHS plugin via "this.vars".
 *    $('#form-id').serialize().some_other_plugin();
 *
 * @return   the jQuery Object
 * @return jQuery
 * @see      ajaxForm(), ajaxSubmit()
 * @author   Mark Constable (markc@renta.net)
 * @author   G. vd Hoven, Mike Alsup, Sam Collett, John Resig
 */
$.fn.serialize = function() {
  var a = [];
  var ok = {INPUT:true, TEXTAREA:true, OPTION:true};

  $('*', this).each(function() {
    var par = this.parentNode;
    var p = par.nodeName.toUpperCase();
    var n = this.name || p == 'OPTGROUP' && par.parentNode.name || p == 'SELECT' && par.name || this.id;

    if ( !n || this.disabled || this.type == 'reset' ||
      (this.type == 'checkbox' || this.type == 'radio') && !this.checked ||
      !ok[this.nodeName.toUpperCase()] ||
      (this.type == 'submit' || this.type == 'image') && this.form.clicked != this ||
      (p == 'SELECT' || p == 'OPTGROUP') && !this.selected ) return;

    a.push({name: n, value: this.value});
  });
  this.vars = a;

  return this;
};


// copy of php function Webride::getAvatarSrc
function getAvatarSrc(nickname, size, avatar) {
  avatar = typeof avatar == 'undefined' ? 0 : avatar;
  if (avatar != 1)
      return '/shared/img/avatar_na_' + size + 'x' + size + '.gif';

  nickname = nickname.toLowerCase();
  avatarSrc = '/data/avatars/users/';
  for (i = 0; i < nickname.length; i++) {
      avatarSrc+= nickname.charAt(i) + '/';
  }
  avatarSrc+= size + 'x' + size + '.jpg';
  return avatarSrc;
};


if (typeof console == 'undefined') {
  // Try to be compatible with other browsers
  // Only use firebug logging when available
  console = new Object;
  console.trace = function() {};
  console.log = function() {};
  console.debug = function() {};
  console.info = function() {};
  console.warn = function() {};
  console.error = function() {};
  console.time = function() {};
  console.timeEnd = function() {};
  console.count = function() {};
};