// creates a proxied array, optionally initialized,
// with optional getter and validate/changed hooks
function proxied_array (spec) {
var proxy = new Proxy(spec.init || [], {
get: function (obj, prop) {
// call proxy when defined
if (spec.get) spec.get.call(obj, prop);
// return appropriately bound proxy functions
if (prop in obj && Function.bind === obj[prop].bind) {
if (spec.changed)
return function bound () {
var tmp = this.slice();
var ret = this[prop].apply(tmp, arguments);
console.log(JSON.stringify({
"event": "method called",
"this": this,
"tmp": tmp,
"method": prop,
"arguments": arguments,
"returning": ret
}));
if (this != tmp && tmp.forEach(spec.validate)) {
this[prop].apply(this, arguments);
if (spec.changed) spec.changed.call(this);
}
return ret;
}.bind(obj);
return obj[prop].bind(obj);
}
// and pass through read requests invisibly
return obj[prop];
},
set: function (array, index, value) {
// call proxy if defined
console.log(JSON.stringify({
"event": "value set",
"array": array,
"index": index,
"value": value
}));
if (spec.validate && spec.validate(value, index, array)) {
array[index] = value;
if (spec.changed) spec.changed.call(array);
}
return value;
}
});
return proxy;
}
// creates an permanent fixed-length proxied array as a named propery
function bind_array (host, property, spec) {
Object.defineProperty(host, property, (function (spec) {
var proxied = new proxied_array(spec);
return {
get: function () { return proxied; },
set: function (d) {
console.log(JSON.stringify({
"event": "array set",
"data": d
}));
if (!Array.isArray(d))
throw new TypeError("cannot change data type");
if (d.length != proxied.length)
throw new TypeError("array is of the wrong length");
if (d.forEach(spec.validate)) {
spec.init = d;
proxied = new proxied_array(spec);
if (spec.changed) spec.changed.call(proxied);
}
}
};
})(spec));
}
// example for testing and learning
bind_array(window, "foo", {
init: [0, 0, 0],
validate: function (value, index, array) {
console.log(JSON.stringify({
"event": "validate",
"value": value,
"index": index,
"array": array
}));
if (isNaN(value - 0))
throw new TypeError("this array only holds numbers");
if ("length" === index)
throw new TypeError("cannot redefine array length");
if (array.length <= index - 0)
throw new RangeError("specified index is out of range");
return true;
},
changed: function () {
console.log(JSON.stringify({
"event": "changed",
"this": this
}));
}
});
// */