var siteurl = 'http://www.tenpintakapuna.co.nz';
var secureurl = 'https://secure.gokiwi.net/tenpintakapuna.co.nz';

//Get all the elements of the given classname of the given tag.
function getElementsByClassName(classname,tag) {
  if(!tag) tag = "*";
  var anchs =  document.getElementsByTagName(tag);
  var total_anchs = anchs.length;
  var regexp = new RegExp('\\b' + classname + '\\b');
  var class_items = new Array()

  for(var i=0;i<total_anchs;i++) { //Go thru all the links seaching for the class name
    var this_item = anchs[i];
    if(regexp.test(this_item.className)) {
      class_items.push(this_item);
    }
  }
  return class_items;
}

function confirmdelete() {
    if (confirm("Are you sure you want to delete this?") == true) {
        return true;
    } else {
        return false;
    }
}

function xyz(c,a,b,s) {
    var s = (s == null) ? true : s;
    var o = '';
    var m = '';
    var m2 = ':otliam';
    for (i = 0; i <= b.length; i++) {o = b.charAt (i) + o;}
    b = o;
    for (i = 0; i <= m2.length; i++) {m = m2.charAt (i) + m;}
    if (!s) {m = '';}
    return m + a + unescape('%'+'4'+'0') + b + '.' + c;
}

function two_dp(num) {
  num = parseFloat(num);
  return(Math.round(num*100)/100);
}

function formatAsMoney(mnt) {
    mnt -= 0;
    mnt = (Math.round(mnt*100))/100;
    return (mnt == Math.floor(mnt)) ? mnt + '.00' : ( (mnt*10 == Math.floor(mnt*10)) ? mnt + '0' : mnt);
}





function validate(val,type) {
    if (type=='email'){
        return val.match(/^(?:^[A-Z0-9._%-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|biz|info|name|aero|biz|info|jobs|museum|name)$)$/i);
        //return (val.indexOf(".") > 2) && (val.indexOf("@") > 0);
    }else if(type=='url'){
        return val.match(/^(?:\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])$/i);
    }else if(type=='integer'){
        //var regex = new RegExp ('[^0-9\.]','gi');
        return val.match(/^-?[0-9]+$/i);
        //var newval=parseFloat(val.replace(regex, ""));
        //if (newval != val) {alert('Only numeric input is allowed in this field');}
        //if (isNaN(newval)) {field.value = '';} else {field.value = newval;}
        //document.forms[theform].elements['fm_cost_'+i].value
    }
}

/*-- [validateEmail] --*/
/*-- Returns true or false based on whether an email address is a valid format --*/
function validateEmail(str) {
  var at="@"
  var dot="."
  var lat=str.indexOf(at)
  var lstr=str.length
  var ldot=str.indexOf(dot)
  if (str.indexOf(at)==-1){return false}
  if (str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr){return false}
  if (str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr){return false}
  if (str.indexOf(at,(lat+1))!=-1){return false}
  if (str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot){return false}
  if (str.indexOf(dot,(lat+2))==-1){return false}
  if (str.indexOf(" ")!=-1){return false}
  return true
}

/*-- [showhide] --*/
function showhide(region) {
    if (document.getElementById) {
        if (document.getElementById(region).style.display == 'none') {
          document.getElementById(region).style.display = 'block';
        } else {
          document.getElementById(region).style.display = 'none';
        }
    } else {
      alert('Sorry, your browser doesn\'t support this');
    }
}

/*-- [showregion] --*/
function showregion(region) {
  if (document.getElementById) {
    document.getElementById(region).style.display = 'block';
  } else {
    alert('Sorry, your browser doesn\'t support this');
  }
}

/*-- [hideregion] --*/
function hideregion(region) {
  if (document.getElementById) {
    document.getElementById(region).style.display = 'none';
  } else {
    alert('Sorry, your browser doesn\'t support this');
  }
}

/**
 * Sets a Cookie with the given name and value.
 *
 * name       Name of the cookie
 * value      Value of the cookie
 * [expires]  Expiration date of the cookie (default: end of current session)
 * [path]     Path where the cookie is valid (default: path of calling document)
 * [domain]   Domain where the cookie is valid
 *              (default: domain of calling document)
 * [secure]   Boolean value indicating if the cookie transmission requires a
 *              secure transmission
 */
function setCookie(name, value, expires, path, domain, secure) {
    document.cookie= name + "=" + escape(value) +
        ((expires) ? "; expires=" + expires.toGMTString() : "") +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
}

/**
 * Gets the value of the specified cookie.
 *
 * name  Name of the desired cookie.
 *
 * Returns a string containing value of specified cookie,
 *   or null if cookie does not exist.
 */
function getCookie(name) {
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1) {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    } else {
        begin += 2;
    }
    var end = document.cookie.indexOf(";", begin);
    if (end == -1) {
        end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
}

/**
 * Deletes the specified cookie.
 *
 * name      name of the cookie
 * [path]    path of the cookie (must be same as path used to create cookie)
 * [domain]  domain of the cookie (must be same as domain used to create cookie)
 */
function deleteCookie(name, path, domain) {
  if (getCookie(name)) {
    document.cookie = name + "=" +
    ((path) ? "; path=" + path : "") +
    ((domain) ? "; domain=" + domain : "") +
    "; expires=Thu, 01-Jan-70 00:00:01 GMT";
  }
}

function isNull(a) {
  return typeof a == 'object' && !a;
}

/*-- [shownode] --*/
function shownode(treename,node) {
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var i = cookiearr.length;
    var controllerhtml = document.getElementById('controller'+node).innerHTML;
    if (document.getElementById) {
    //show the node
    document.getElementById('node'+node).style.display = 'block';
    //Change the controller
        controllerhtml = controllerhtml.replace("+","-");
        controllerhtml = controllerhtml.replace("plus.","minus.");
        controllerhtml = controllerhtml.replace("closed.","open.");
        document.getElementById('controller'+node).innerHTML = controllerhtml;
    //first, clear any existing occurances
        for (var n=0; n<cookiearr.length; n++) {
            if (cookiearr[n] == node) {cookiearr[n] = ''}
        }
        cookiearr[i] = node;
        cookiecontents = cookiearr.join(".");
    }
    setCookie(treename+'tree',cookiecontents,'', '/');
}

/*-- [hidenode] --*/
function hidenode(treename,node)
{
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var i = cookiearr.length;
    var controllerhtml = document.getElementById('controller'+node).innerHTML;
    if (document.getElementById) {
    //hide the node
        document.getElementById('node'+node).style.display = 'none';
    //Change the controller
        controllerhtml = controllerhtml.replace("-","+");
        controllerhtml = controllerhtml.replace("minus.","plus.");
        controllerhtml = controllerhtml.replace("open.","closed.");
        document.getElementById('controller'+node).innerHTML = controllerhtml;
        for (var n=0; n<cookiearr.length; n++) {
            if (cookiearr[n] == node) {cookiearr[n] = ''}
        }
        cookiecontents = cookiearr.join(".");
    //cookiecontents = cookiecontents.replace(node,'');
    //cookiecontents = cookiecontents.replace('..','.');
    } else {
    alert('Sorry, your browser doesn\'t support this');
  }
    setCookie(treename+'tree',cookiecontents,'', '/');
}

/*-- [showhidenode] --*/
function showhidenode(treename,node)
{
    //alert(node);
    if ( (document.getElementById('node'+node).style.display == 'none') || (document.getElementById('node'+node).style.display == '') ) {
        shownode(treename,node);
    } else {
        hidenode(treename,node);
    }
}

/*-- [showthesenodes] --*/
function showthesenodes(treename,nodes) {
    var showarr = nodes.split('.');
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var shortlength = showarr.length - 1;

    //hide everything
    for (var n=0; n<cookiearr.length; n++) {
        if ((cookiearr[n] != '') && (cookiearr[n] != showarr[shortlength])) {hidenode(treename,cookiearr[n]);}
    }
    //Show specified nodes
    for (var n=0; n<shortlength; n++) {
        if (showarr[n] != '') {shownode(treename,showarr[n]);}
    }
    //Show / Hide last element
    if (showarr[shortlength] != '') {showhidenode(treename,showarr[shortlength]);}
}

/*-- [loadtree] --*/
function loadtree(treename) {
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    //alert(cookiecontents);
    for (var n=0; n<cookiearr.length; n++) {
        if (cookiearr[n] != '') {shownode(treename,cookiearr[n]);}
    }
}


// return the value of the radio button that is checked
// return an empty string if none are checked, or
// there are no radio buttons
function getCheckedValue(radioObj) {
    if(!radioObj)
        return "";
    var radioLength = radioObj.length;
    if(radioLength == undefined)
        if(radioObj.checked)
            return radioObj.value;
        else
            return "";
    for(var i = 0; i < radioLength; i++) {
        if(radioObj[i].checked) {
            return radioObj[i].value;
        }
    }
    return "";
}

// set the radio button with the given value as being checked
// do nothing if there are no radio buttons
// if the given value does not exist, all the radio buttons
// are reset to unchecked
function setCheckedValue(radioObj, newValue) {
    if(!radioObj)
        return;
    var radioLength = radioObj.length;
    if(radioLength == undefined) {
        radioObj.checked = (radioObj.value == newValue.toString());
        return;
    }
    for(var i = 0; i < radioLength; i++) {
        radioObj[i].checked = false;
        if(radioObj[i].value == newValue.toString()) {
            radioObj[i].checked = true;
        }
    }
}

// function parameters are: field - the string field, count - the field for remaining characters number and max - the maximum number of characters
function countDown(fieldid, countid, max) {
  field = document.getElementById(fieldid);
  count = document.getElementById(countid);
  count.value = max - field.value.length;
}

function removeclass(id,c) {
if (document.getElementById(id)) {
  var rep=document.getElementById(id).className.match(' '+c)?' '+c:c;
  document.getElementById(id).className= document.getElementById(id).className.replace(rep,'');
  }
}

function addclass(id,c) {
  if (document.getElementById(id)) {
  removeclass(id,c);
  document.getElementById(id).className += ' '+c;
  }
}

/* same as above, but based on the object, not ID */
function removeClass(object,c) {
if (object) {
  var rep=object.className.match(' '+c)?' '+c:c;
  object.className= object.className.replace(rep,'');
  }
}

function addClass(object,c) {
  if (object) {
  removeClass(object,c);
  object.className += ' '+c;
  }
}

/* For attaching code to events without overwriting existing ones */
function addEvent(obj, evType, fn){
 if (obj.addEventListener){
   obj.addEventListener(evType, fn, false);
   return true;
 } else if (obj.attachEvent){
   var r = obj.attachEvent("on"+evType, fn);
   return r;
 } else {
   return false;
 }
}

function nl2br(myString){
  return myString.replace( /\n/g, '<br />\n' );
}


//Get all the elements of the given classname of the given tag.
function getElementsByClassName(classname,tag) {
  if(!tag) tag = "*";
  var anchs =  document.getElementsByTagName(tag);
  var total_anchs = anchs.length;
  var regexp = new RegExp('\\b' + classname + '\\b');
  var class_items = new Array()

  for(var i=0;i<total_anchs;i++) { //Go thru all the links seaching for the class name
    var this_item = anchs[i];
    if(regexp.test(this_item.className)) {
      class_items.push(this_item);
    }
  }
  return class_items;
}

function confirmdelete() {
    if (confirm("Are you sure you want to delete this?") == true) {
        return true;
    } else {
        return false;
    }
}

function xyz(c,a,b,s) {
    var s = (s == null) ? true : s;
    var o = '';
    var m = '';
    var m2 = ':otliam';
    for (i = 0; i <= b.length; i++) {o = b.charAt (i) + o;}
    b = o;
    for (i = 0; i <= m2.length; i++) {m = m2.charAt (i) + m;}
    if (!s) {m = '';}
    return m + a + unescape('%'+'4'+'0') + b + '.' + c;
}

function two_dp(num) {
  num = parseFloat(num);
  return(Math.round(num*100)/100);
}

function formatAsMoney(mnt) {
    mnt -= 0;
    mnt = (Math.round(mnt*100))/100;
    return (mnt == Math.floor(mnt)) ? mnt + '.00' : ( (mnt*10 == Math.floor(mnt*10)) ? mnt + '0' : mnt);
}





function validate(val,type) {
    if (type=='email'){
        return val.match(/^(?:^[A-Z0-9._%-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|biz|info|name|aero|biz|info|jobs|museum|name)$)$/i);
        //return (val.indexOf(".") > 2) && (val.indexOf("@") > 0);
    }else if(type=='url'){
        return val.match(/^(?:\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])$/i);
    }else if(type=='integer'){
        //var regex = new RegExp ('[^0-9\.]','gi');
        return val.match(/^-?[0-9]+$/i);
        //var newval=parseFloat(val.replace(regex, ""));
        //if (newval != val) {alert('Only numeric input is allowed in this field');}
        //if (isNaN(newval)) {field.value = '';} else {field.value = newval;}
        //document.forms[theform].elements['fm_cost_'+i].value
    }
}

/*-- [validateEmail] --*/
/*-- Returns true or false based on whether an email address is a valid format --*/
function validateEmail(str) {
  var at="@"
  var dot="."
  var lat=str.indexOf(at)
  var lstr=str.length
  var ldot=str.indexOf(dot)
  if (str.indexOf(at)==-1){return false}
  if (str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr){return false}
  if (str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr){return false}
  if (str.indexOf(at,(lat+1))!=-1){return false}
  if (str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot){return false}
  if (str.indexOf(dot,(lat+2))==-1){return false}
  if (str.indexOf(" ")!=-1){return false}
  return true
}

/*-- [showhide] --*/
function showhide(region) {
    if (document.getElementById) {
        if (document.getElementById(region).style.display == 'none') {
          document.getElementById(region).style.display = 'block';
        } else {
          document.getElementById(region).style.display = 'none';
        }
    } else {
      alert('Sorry, your browser doesn\'t support this');
    }
}

/*-- [showregion] --*/
function showregion(region) {
  if (document.getElementById) {
    document.getElementById(region).style.display = 'block';
  } else {
    alert('Sorry, your browser doesn\'t support this');
  }
}

/*-- [hideregion] --*/
function hideregion(region) {
  if (document.getElementById) {
    document.getElementById(region).style.display = 'none';
  } else {
    alert('Sorry, your browser doesn\'t support this');
  }
}

/**
 * Sets a Cookie with the given name and value.
 *
 * name       Name of the cookie
 * value      Value of the cookie
 * [expires]  Expiration date of the cookie (default: end of current session)
 * [path]     Path where the cookie is valid (default: path of calling document)
 * [domain]   Domain where the cookie is valid
 *              (default: domain of calling document)
 * [secure]   Boolean value indicating if the cookie transmission requires a
 *              secure transmission
 */
function setCookie(name, value, expires, path, domain, secure) {
    document.cookie= name + "=" + escape(value) +
        ((expires) ? "; expires=" + expires.toGMTString() : "") +
        ((path) ? "; path=" + path : "") +
        ((domain) ? "; domain=" + domain : "") +
        ((secure) ? "; secure" : "");
}

/**
 * Gets the value of the specified cookie.
 *
 * name  Name of the desired cookie.
 *
 * Returns a string containing value of specified cookie,
 *   or null if cookie does not exist.
 */
function getCookie(name) {
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1) {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    } else {
        begin += 2;
    }
    var end = document.cookie.indexOf(";", begin);
    if (end == -1) {
        end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
}

/**
 * Deletes the specified cookie.
 *
 * name      name of the cookie
 * [path]    path of the cookie (must be same as path used to create cookie)
 * [domain]  domain of the cookie (must be same as domain used to create cookie)
 */
function deleteCookie(name, path, domain) {
  if (getCookie(name)) {
    document.cookie = name + "=" +
    ((path) ? "; path=" + path : "") +
    ((domain) ? "; domain=" + domain : "") +
    "; expires=Thu, 01-Jan-70 00:00:01 GMT";
  }
}

function isNull(a) {
  return typeof a == 'object' && !a;
}

/*-- [shownode] --*/
function shownode(treename,node) {
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var i = cookiearr.length;
    var controllerhtml = document.getElementById('controller'+node).innerHTML;
    if (document.getElementById) {
    //show the node
    document.getElementById('node'+node).style.display = 'block';
    //Change the controller
        controllerhtml = controllerhtml.replace("+","-");
        controllerhtml = controllerhtml.replace("plus.","minus.");
        controllerhtml = controllerhtml.replace("closed.","open.");
        document.getElementById('controller'+node).innerHTML = controllerhtml;
    //first, clear any existing occurances
        for (var n=0; n<cookiearr.length; n++) {
            if (cookiearr[n] == node) {cookiearr[n] = ''}
        }
        cookiearr[i] = node;
        cookiecontents = cookiearr.join(".");
    }
    setCookie(treename+'tree',cookiecontents,'', '/');
}

/*-- [hidenode] --*/
function hidenode(treename,node)
{
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var i = cookiearr.length;
    var controllerhtml = document.getElementById('controller'+node).innerHTML;
    if (document.getElementById) {
    //hide the node
        document.getElementById('node'+node).style.display = 'none';
    //Change the controller
        controllerhtml = controllerhtml.replace("-","+");
        controllerhtml = controllerhtml.replace("minus.","plus.");
        controllerhtml = controllerhtml.replace("open.","closed.");
        document.getElementById('controller'+node).innerHTML = controllerhtml;
        for (var n=0; n<cookiearr.length; n++) {
            if (cookiearr[n] == node) {cookiearr[n] = ''}
        }
        cookiecontents = cookiearr.join(".");
    //cookiecontents = cookiecontents.replace(node,'');
    //cookiecontents = cookiecontents.replace('..','.');
    } else {
    alert('Sorry, your browser doesn\'t support this');
  }
    setCookie(treename+'tree',cookiecontents,'', '/');
}

/*-- [showhidenode] --*/
function showhidenode(treename,node)
{
    //alert(node);
    if ( (document.getElementById('node'+node).style.display == 'none') || (document.getElementById('node'+node).style.display == '') ) {
        shownode(treename,node);
    } else {
        hidenode(treename,node);
    }
}

/*-- [showthesenodes] --*/
function showthesenodes(treename,nodes) {
    var showarr = nodes.split('.');
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    var shortlength = showarr.length - 1;

    //hide everything
    for (var n=0; n<cookiearr.length; n++) {
        if ((cookiearr[n] != '') && (cookiearr[n] != showarr[shortlength])) {hidenode(treename,cookiearr[n]);}
    }
    //Show specified nodes
    for (var n=0; n<shortlength; n++) {
        if (showarr[n] != '') {shownode(treename,showarr[n]);}
    }
    //Show / Hide last element
    if (showarr[shortlength] != '') {showhidenode(treename,showarr[shortlength]);}
}

/*-- [loadtree] --*/
function loadtree(treename) {
    var cookiecontents = getCookie(treename+'tree');
    if (isNull(cookiecontents)) {var cookiecontents = '';}
    var cookiearr = cookiecontents.split('.');
    //alert(cookiecontents);
    for (var n=0; n<cookiearr.length; n++) {
        if (cookiearr[n] != '') {shownode(treename,cookiearr[n]);}
    }
}


// return the value of the radio button that is checked
// return an empty string if none are checked, or
// there are no radio buttons
function getCheckedValue(radioObj) {
    if(!radioObj)
        return "";
    var radioLength = radioObj.length;
    if(radioLength == undefined)
        if(radioObj.checked)
            return radioObj.value;
        else
            return "";
    for(var i = 0; i < radioLength; i++) {
        if(radioObj[i].checked) {
            return radioObj[i].value;
        }
    }
    return "";
}

// set the radio button with the given value as being checked
// do nothing if there are no radio buttons
// if the given value does not exist, all the radio buttons
// are reset to unchecked
function setCheckedValue(radioObj, newValue) {
    if(!radioObj)
        return;
    var radioLength = radioObj.length;
    if(radioLength == undefined) {
        radioObj.checked = (radioObj.value == newValue.toString());
        return;
    }
    for(var i = 0; i < radioLength; i++) {
        radioObj[i].checked = false;
        if(radioObj[i].value == newValue.toString()) {
            radioObj[i].checked = true;
        }
    }
}

// function parameters are: field - the string field, count - the field for remaining characters number and max - the maximum number of characters
function countDown(fieldid, countid, max) {
  field = document.getElementById(fieldid);
  count = document.getElementById(countid);
  count.value = max - field.value.length;
}

function removeclass(id,c) {
if (document.getElementById(id)) {
  var rep=document.getElementById(id).className.match(' '+c)?' '+c:c;
  document.getElementById(id).className= document.getElementById(id).className.replace(rep,'');
  }
}

function addclass(id,c) {
  if (document.getElementById(id)) {
  removeclass(id,c);
  document.getElementById(id).className += ' '+c;
  }
}

/* same as above, but based on the object, not ID */
function removeClass(object,c) {
if (object) {
  var rep=object.className.match(' '+c)?' '+c:c;
  object.className= object.className.replace(rep,'');
  }
}

function addClass(object,c) {
  if (object) {
  removeClass(object,c);
  object.className += ' '+c;
  }
}

/* For attaching code to events without overwriting existing ones */
function addEvent(obj, evType, fn){
 if (obj.addEventListener){
   obj.addEventListener(evType, fn, false);
   return true;
 } else if (obj.attachEvent){
   var r = obj.attachEvent("on"+evType, fn);
   return r;
 } else {
   return false;
 }
}

function nl2br(myString){
  return myString.replace( /\n/g, '<br />\n' );
}

/**
 * @ Original code by by Binny V A, Original version: 2.00.A 
 * @ http://www.openjs.com/scripts/events/keyboard_shortcuts/
 * @ Original License : BSD
 * @ jQuery Plugin by Tzury Bar Yochay 
        tzury.by@gmail.com
        evalinux.wordpress.com
        facebook.com/profile.php?id=513676303
 * @ jQuery Plugin version Beta (0.0.2)
 * @ License: Whatever feats in between jQuery-License + BSD-License

TODO:
    add queue support (as in gmail) e.g. 'x' then 'y', etc.
    add mouse + mouse wheel events.

Usage:
    jQuery.hotkeys.add('Ctrl+c', function(){ alert('copy anyone?');});
    jQuery.hotkeys.add('Ctrl+c', {target:'div#editor', type:'keyup', propagate: true},function(){ alert('copy anyone?');});
    jQuery.hotkeys.remove('Ctrl+c'); 
    */
    
(function (jQuery){
    this.version = '(beta)(0.0.2)';
	this.all = {};
    this.special_keys = {
        27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll', 20: 'capslock', 
        144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',35:'end', 33: 'pageup', 
        34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down', 112:'f1',113:'f2', 114:'f3', 
        115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8', 120:'f9', 121:'f10', 122:'f11', 123:'f12'};
        
    this.shift_nums = { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&", 
        "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<", 
        ".":">",  "/":"?",  "\\":"|" };

    this.add = function(combi, options, callback) {
        if (jQuery.isFunction(options)){
            callback = options;
            options = {};
        }
        var opt = {},
            defaults = {type: 'keydown', propagate: false, disableInInput: false, target: jQuery('html')[0]},
            that = this;
        opt = jQuery.extend( opt , defaults, options || {} );
        combi = combi.toLowerCase();        
        
        // inspect if key keystroke matches 
        var inspector = function(event) {
            event = jQuery.event.fix(event); // jQuery event normalization.
            var element = event.target;
            // @ TextNode -> nodeType == 3
            element = (element.nodeType==3) ? element.parentNode : element;
            
            if(opt['disableInInput']) { // Disable shortcut keys in Input, Textarea fields
                var target = jQuery(element);
                if( target.is("input") || target.is("textarea")){
                    return;
                }
            }
            var code = event.which,
                type = event.type,
                character = String.fromCharCode(code).toLowerCase(),
                special = that.special_keys[code],
                shift = event.shiftKey,
                ctrl = event.ctrlKey,
                alt= event.altKey,
                propagate = true, // default behaivour
                mapPoint = null;
            
            // in opera + safari, the event.target is unpredictable.
            // for example: 'keydown' might be associated with HtmlBodyElement 
            // or the element where you last clicked with your mouse.
            if (jQuery.browser.opera || jQuery.browser.safari){
                while (!that.all[element] && element.parentNode){
                    element = element.parentNode;
                }
            }
            
            var cbMap = that.all[element].events[type].callbackMap;
            if(!shift && !ctrl && !alt) { // No Modifiers
                mapPoint = cbMap[special] ||  cbMap[character]
			}
            // deals with combinaitons (alt|ctrl|shift+anything)
            else{
                var modif = '';
                if(alt) modif +='alt+';
                if(ctrl) modif+= 'ctrl+';
                if(shift) modif += 'shift+';
                // modifiers + special keys or modifiers + characters or modifiers + shift characters
                mapPoint = cbMap[modif+special] || cbMap[modif+character] || cbMap[modif+that.shift_nums[character]]
            }
            if (mapPoint){
                mapPoint.cb(event);
                if(!mapPoint.propagate) {
                    event.stopPropagation();
                    event.preventDefault();
                    return false;
                }
            }
		};        
        // first hook for this element
        if (!this.all[opt.target]){
            this.all[opt.target] = {events:{}};
        }        
        if (!this.all[opt.target].events[opt.type]){
            this.all[opt.target].events[opt.type] = {callbackMap: {}}
            jQuery.event.add(opt.target, opt.type, inspector);
        }        
        this.all[opt.target].events[opt.type].callbackMap[combi] =  {cb: callback, propagate:opt.propagate};                
        return jQuery;
	};    
    this.remove = function(exp, opt) {
        opt = opt || {};
        target = opt.target || jQuery('html')[0];
        type = opt.type || 'keydown';
		exp = exp.toLowerCase();        
        delete this.all[target].events[type].callbackMap[exp]        
        return jQuery;
	};
    jQuery.hotkeys = this;
    return jQuery;    
})(jQuery);
