code: function wrap(fn, before, after, advisor) {
      var ignoredKeys = { prototype: 1 };
  
      function wrapper() {
        var _before = wrapper[BEFORE],
            bLen    = _before.length,
            _after  = wrapper[AFTER],
            aLen    = _after.length,
            _fn     = wrapper[ORIGIN],
            ret, r;
  
        // Invoke before, if it returns { $skip: true } then skip fn() and after() and returns $data
        while (bLen--) {
          r = _before[bLen].apply(this, arguments);
          if (mapOrNil(r) && r.$skip === true) {
            return r.$data;
          }
        }
  
        // Invoke fn, save return value
        ret = _fn.apply(this, arguments);
  
        while (aLen--) {
          r = _after[aLen].apply(this, arguments);
          if (mapOrNil(r) && r.$skip === true) {
            return ret;
          }
        }
        return ret;
      }
  
      // wrapper exists? reuse it
      if (fn[WRAPPER] === fn) {
        fn[BEFORE].push(before);
        fn[AFTER].push(after);
        fn[ADVISOR].push(advisor);
        return fn;
      }
  
      // create a reusable wrapper structure
      extend(wrapper, fn, 0, true);
      if (classOrNil(fn)) {
        wrapper.prototype = fn.prototype;
  
        // this destroys the origin of wrapper[ORIGIN], in theory, prototype.constructor should point
        // to the class constructor itself, but it's no harm to not let that happen
        // wrapper.prototype.constructor = wrapper;
      }
  
      wrapper[BEFORE]  = [ before ];
      wrapper[AFTER]   = [ after ];
      wrapper[ORIGIN]  = fn;
      wrapper[ADVISOR] = [ advisor ];
      wrapper[WRAPPER] = wrapper;
      wrapper.$super   = fn.$super;
      wrapper.$superp  = fn.$superp;
      return wrapper;
    }
docs: Wrap a function with before & after.