

function print(obj, maxDepth, prefix){
   var result = '';
   if (!prefix) prefix='';
   for(var key in obj){
       if (typeof obj[key] == 'object'){
           if (maxDepth !== undefined && maxDepth <= 1){
               result += (prefix + key + '=object [max depth reached]\n');
           } else {
               result += print(obj[key], (maxDepth) ? maxDepth - 1: maxDepth, prefix + key + '.');
           }
       } else {
           result += (prefix + key + '=' + obj[key] + '\n');
       }
   }
   return result;
}

function makePath(obj, path) {
  var root = obj;
  for(var i in path) {
    step = path[i];
    if(obj[step] == undefined) {
      obj[step] = {  };
    }
    obj = obj[step];
  }
  return root;
}

(function($) {

$.getHandler = function(elem, matchType, handlerKey, saved) {
  var elemData = $.data(elem);
  if(elemData != null) {
    var events = elemData.events;
    if(saved && elemData.savedHandlers) events = elemData.savedHandlers.events;
    if(events) {
      return events[matchType][handlerKey]['handler'];
    }
  }
  return undefined;
}

$.setHandlers = function(elem, newHandler, matchType, matchFunc) {
  var paths = $.getHandlerPaths(elem, matchType, matchFunc);
  var elemData = $.data(elem);
  for(var i=0; i < paths.length; i++) {
    var path = paths[i];
    elemData["events"][path.type][path.key]['handler'] = newHandler;
  }
}

$.saveHandlers = function(elem, matchType, matchFunc) {
  var paths = $.getHandlerPaths(elem, matchType, matchFunc);
  var elemData = $.data(elem);
  for(var i=0; i < paths.length; i++) {
    var path = paths[i];
    var handler = $.getHandler(elem, path.type, path.key);
    if(elemData.savedHandlers == null) {
      elemData.savedHandlers = makePath({ }, ["events", path.type, path.key, "handler"]);
      /*elemData.savedHandlers = { events: {} };
      elemData.savedHandlers["events"][path.type] = {};
      elemData.savedHandlers["events"][path.type][path.key] = {} */
      elemData.savedHandlers["events"][path.type][path.key].handler = handler;
      //alert(print(elemData));
    }
    else {
      elemData.savedHandlers = makePath(elemData.savedHandlers, ["events", path.type, path.key, "handler"]);
      //if(elemData.savedHandlers["events"][path.type] == undefined) elemData.savedHandlers["events"][path.type] = {};
      //if(elemData.savedHandlers["events"][path.type][path.key] == undefined) elemData.savedHandlers["events"][path.type] = {};
      elemData.savedHandlers["events"][path.type][path.key].handler = handler;
      //alert(print(elemData));
    }
  }
}

$.restoreHandlers = function(elem, matchType, matchFunc) {
  var paths = $.getHandlerPaths(elem, matchType, matchFunc, true);
  var elemData = $.data(elem);
  for(var i=0; i < paths.length; i++) {
    var path = paths[i];
    elemData.events[path.type][path.key]['handler'] = $.getHandler(elem, path.type, path.key, true);
    if(elemData.savedHandlers) elemData.savedHandlers.events[path.type][path.key] = undefined;
  }
}

$.replaceHandlers = function(elem, newFunc, matchType, matchFunc) {
  $.saveHandlers(elem, matchType, matchFunc);
  $.setHandlers(elem, newFunc, matchType, matchFunc);
}

$.getHandlerPaths = function(elem, matchType, matchFunc, saved) {
  var result = [];
  var elemData = $.data(elem);
  if(elemData != null) {
    var events = elemData.events;
    if(saved && elemData.savedHandlers) events = elemData.savedHandlers.events;
    if(events) {
      if(matchType && matchFunc) {
        // suppress all bindings of this type and function
        var handlers = events[matchType];
        for(var handlerKey in handlers) {
          var handler = events[matchType][handlerKey];
          if(handler == matchFunc) {
            result.push({
              "type": matchType,
              "key": handlerKey
            });
          }
        }
      }
      else if(matchType && !matchFunc) {
        // suppress all bindings of this type
        var handlers = events[matchType];
        for(var handlerKey in handlers) {
          var handler = events[matchType][handlerKey];
          result.push({
            "type": matchType,
            "key": handlerKey
          });
        }
      }
      else if(!matchType && matchFunc) {
        // suppress all bindings of this function
        for(var eventType in events) {
          var handlers = events[eventType];
          for(var handlerKey in handlers) {
            var handler = events[eventType][handlerKey];
            if(handler == matchFunc) {
              result.push({
                "type": eventType,
                "key": handlerKey
              });
            }
          }
        }
      }
      else if(!matchType && !matchFunc) {
        // suppress all bindings
        for(var eventType in events) {
          var handlers = events[eventType];
          for(var handlerKey in handlers) {
            var handler = events[eventType][handlerKey];
            result.push({
              "type": eventType,
              "key": handlerKey
            });
          }
        }
      }
    }
  }
  return result;
}

})(jQuery);

(function($) {
$.fn.handlers = function(matchType, matchFunc) {
  var result = [];
  $(this).each(function() {
    var paths = $.getHandlerPaths(this, matchType, matchFunc);
    for(var i=0; i < paths.length; i++) {
      var path = paths[i];
      var handler = $.getHandler(this, path.type, path.key);
      if(handler) result.push(handler);
    }
  });
  return result;
}

$.fn.replacehandlers = function(newFunc, matchType, matchFunc, time) {
  if(!newFunc) newFunc = function() {};
  $(this).each(function() {
    var elem = this;
    $.replaceHandlers(elem, newFunc, matchType, matchFunc);
    if(time) {
      setTimeout(function() {
        $.restoreHandlers(elem, matchType, matchFunc);
      }, time);
    }
  });
}

$.fn.restorehandlers = function(matchType, matchFunc) {
  $(this).each(function() {
    $.restoreHandlers(this, matchType, matchFunc);
  });
}

})(jQuery);
