1 /**
  2  * wrap.js
  3  *
  4  * Wrap provides the proxy object that is exposed to the server.
  5  * Changes/additions to the object's properties and nested objects
  6  * trigger the store function to be called.
  7  *
  8  */
  9 
 10 var Proxy = require('node-proxy');
 11 
 12 exports.wrap = function (entity) {
 13   function wrapRoot(path, proto) {
 14     return Proxy.create({
 15       get : function (receiver, name) {
 16         if (Array.isArray(proto) && name === 'length') {
 17           var arr = entity.scopeTable.get(path);
 18           return entity.scopeTable.flagAsArray(path, arr.length && (1 + 1 * arr[arr.length - 1]));
 19         }
 20         var fqn = path + '.' + name;
 21         var returnObj = entity.get(fqn);
 22         if (returnObj && typeof returnObj === 'object') {
 23           var p = (entity.scopeTable.arrays[fqn] === undefined) ? Object.prototype : Array.prototype;
 24           if (p === Array.prototype) {
 25             entity.scopeTable.flagAsArray(fqn, returnObj[returnObj.length - 1] || 0);
 26           }
 27           return wrapRoot(fqn, p);
 28         }
 29         if (returnObj === undefined && proto[name]) {
 30           return proto[name];
 31         }
 32         return returnObj;
 33       },
 34       set : function (receiver, name, value) {
 35         var fqn = path + '.' + name;
 36         if (proto === Array.prototype) {
 37           var len = 1 * entity.scopeTable.arrays[path] || 0;
 38           if (name === 'length') {
 39             return entity.scopeTable.flagAsArray(path, value);
 40           }
 41           if (name > len) {
 42             entity.scopeTable.flagAsArray(path, 1 + name);
 43             while (len <= name) {
 44               receiver[len++] = undefined;
 45             }
 46           }
 47         }
 48         entity.set(fqn, value);
 49         if (!value || typeof value !== 'object') {
 50           return value;
 51         }
 52         if (Array.isArray(value)) {
 53           entity.scopeTable.flagAsArray(fqn, value.length);
 54         }
 55         return wrapRoot(fqn, value.constructor.prototype);
 56       },
 57       enumerate : function () {
 58         return entity.scopeTable.get(path);
 59       },
 60       hasOwn : function (name) {
 61         return entity.scopeTable.get(path + '.' + name) !== undefined;
 62       },
 63       delete : function (name) {
 64         var fqn = path + '.' + name;
 65         entity.deleteVar(fqn);
 66       },
 67       fix : function () {
 68         return undefined;
 69       }
 70     }, proto);
 71   }
 72   return wrapRoot('now', Object.prototype);
 73 };
 74