1 var Proxy = require("node-proxy"), merge = require("./object").merge;
  2 
  3 var handlerMaker = function(obj) {
  4     return {
  5         getOwnPropertyDescriptor: function(name) {
  6             var desc = Object.getOwnPropertyDescriptor(obj, name);
  7             // a trapping proxy's properties must always be configurable
  8             if (desc !== undefined) {
  9                 desc.configurable = true;
 10             }
 11             return desc;
 12         },
 13         getPropertyDescriptor:  function(name) {
 14             var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
 15             // a trapping proxy's properties must always be configurable
 16             if (desc !== undefined) {
 17                 desc.configurable = true;
 18             }
 19             return desc;
 20         },
 21         getOwnPropertyNames: function() {
 22             return Object.getOwnPropertyNames(obj);
 23         },
 24         getPropertyNames: function() {
 25             return Object.getPropertyNames(obj);                // not in ES5
 26         },
 27         defineProperty: function(name, desc) {
 28             Object.defineProperty(obj, name, desc);
 29         },
 30         delete:       function(name) {
 31             return delete obj[name];
 32         },
 33         fix:          function() {
 34             if (Object.isFrozen(obj)) {
 35                 var result = {};
 36                 Object.getOwnPropertyNames(obj).forEach(function(name) {
 37                     result[name] = Object.getOwnPropertyDescriptor(obj, name);
 38                 });
 39                 return result;
 40             }
 41             // As long as obj is not frozen, the proxy won't allow itself to be fixed
 42             return undefined; // will cause a TypeError to be thrown
 43         },
 44 
 45         has: function(name) {
 46             return name in obj;
 47         },
 48         hasOwn: function(name) {
 49             return ({}).hasOwnProperty.call(obj, name);
 50         },
 51         get: function(receiver, name) {
 52             return obj[name];
 53         },
 54         set: function(receiver, name, val) {
 55             obj[name] = val;
 56             return true;
 57         }, // bad behavior when set fails in non-strict mode
 58         enumerate:    function() {
 59             var result = [];
 60             for (var name in obj) {
 61                 result.push(name);
 62             }
 63             return result;
 64         },
 65         keys: function() {
 66             return Object.keys(obj);
 67         }
 68 
 69     };
 70 };
 71 
 72 var noSuchMethodHandler = function(obj, handler) {
 73     return {
 74         get: function(receiver, name) {
 75             return obj[name] ? obj[name] : handler.call(obj, name);
 76         }
 77     }
 78 };
 79 
 80 /**
 81  * Creates a proxy for an object.
 82  * @param obj
 83  * @param opts
 84  */
 85 exports.handlerProxy = function(obj, opts) {
 86     opts = opts || {};
 87     return  Proxy.create(merge(handlerMaker(obj), opts));
 88 };
 89 
 90 /**
 91  * creates a method missing proxy for an object
 92  * @param obj
 93  * @param handler
 94  * @param opts
 95  */
 96 exports.methodMissing = function(obj, handler, opts) {
 97     opts = opts || {};
 98     return  Proxy.create(merge(handlerMaker(obj), noSuchMethodHandler(obj, handler)), opts);
 99 };
100 
101 exports.isProxy = function(obj){
102     var undef;
103     return obj !== undef && obj !== null && Proxy.isProxy(obj);
104 }
105 
106 /**
107  * Creates a function proxy for an object.
108  * @param obj
109  * @param handler
110  * @param opts
111  */
112 exports.createFunctionWrapper = function(obj, handler, opts) {
113     var ret = Proxy.createFunction(handlerMaker(obj), handler);
114     if(opts){
115         Proxy.setPrototype(ret, opts);
116     }
117     return ret;
118 };
119 
120 
121 exports.Proxy = Proxy;