michael.lee.theriot at gmail.com (2016-03-18T21:49:04.209Z)
> Michael’s preferred approach also introduces observable irregularity into
> the standard JS inheritance model for ordinary objects.
> Consider an object created using Michael’s preferred approach:
> ```js
> var arr = [0, 1];
> console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own property
> var subArr = Object.create(arr);
> console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties
> of proto visible from ordinary objects
> var b = new ArrayView2(arr);
> console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array
> elements visible as if properties of b
> var subB= Object.create(b);
> console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties
> of proto is not visible from subB
> ```
I think this relates to the original concern; if you could pass the
receiver this could be resolved. That still leaves `getOwnPropertyNames`
reporting wrong values though and I see no foreseeable way to resolve that
without returning an actual proxy itself.
The reason I'm trying this approach is because I read on the MDN that when
used in the prototype a `receiver` argument is passed that references the
instance, so I assumed this was the intent behind it. The only other
explanation I could think of is that proxies have a receiver to mimic the
`Reflect.set`/`Reflect.get` methods which need a receiver for
getters/setters to work properly, not so you can use them on the prototype
chain.
The other case I would make is every instance would have an identical
proxy, and it just makes sense to put that on the prototype for the same
reasons you put shared methods/properties there.
> Note that we are not really talking about a new capability here. Michael’s
> first design shows that ES proxies already have the capability to implement
> the object level semantics he desires.
To be fair I had several obstacles with inheritance using the first version.
```js
var wm1 = new WeakMap();
function A() {
wm1.set(this, {});
return new Proxy(this, {});
}
var wm2 = new WeakMap();
function B() {
A.call(this);
wm2.set(this, {});
return new Proxy(this, {});
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // false
wm2.has(b); // true
```
As you can see storing a reference to `this` can't work anymore, since we
actually return a proxy. You can try to work around this...
```js
var wm1 = new WeakMap();
function A() {
let self = this;
if(new.target === A) {
self = new Proxy(this, {});
}
wm1.set(self, {});
return self;
}
var wm2 = new WeakMap();
function B() {
let self = this;
if(new.target === B) {
self = new Proxy(this, {});
}
A.call(self);
wm2.set(self, {});
return self;
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // true
wm2.has(b); // true
```
But then problems arise because the new proxy doesn't go through the old
proxy. So anything guaranteed by A()'s proxy is not guaranteed by B()'s
proxy.
```js
var wm1 = new WeakMap();
function A() {
let self = this;
if(new.target === A) {
self = new Proxy(this, {
get: (target, property, receiver) => property === 'bacon' || target[property]
});
}
wm1.set(self, {});
return self;
}
var wm2 = new WeakMap();
function B() {
let self = this;
if(new.target === B) {
self = new Proxy(this, {
get: (target, property, receiver) => property === 'ham' || target[property]
});
}
A.call(self);
wm2.set(self, {});
return self;
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // true
wm2.has(b); // true
a.bacon; // true
a.ham; // undefined
b.bacon; // undefined
b.ham; // true
```
(I'm open to solutions on this particular case... One that doesn't require
me to leak the handler of the A proxy)
Ultimately I can actually achieve both what I want with the ArrayView
example and inheritance by using a **lot** of `defineProperty` calls on
`this` in the constructor, but performance is a disaster as you might
expect.michael.lee.theriot at gmail.com (2016-03-18T21:47:51.517Z)
> Michael’s preferred approach also introduces observable irregularity into
> the standard JS inheritance model for ordinary objects.
> Consider an object created using Michael’s preferred approach:
> ```js
> var arr = [0, 1];
> console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own property
> var subArr = Object.create(arr);
> console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties
> of proto visible from ordinary objects
> var b = new ArrayView2(arr);
> console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array
> elements visible as if properties of b
> var subB= Object.create(b);
> console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties
> of proto is not visible from subB
> ```
I think this relates to the original concern; if you could pass the
receiver this could be resolved. That still leaves `getOwnPropertyNames`
reporting wrong values though and I see no foreseeable way to resolve that
without returning an actual proxy itself.
The reason I'm trying this approach is because I read on the MDN that when
used in the prototype a `receiver` argument is passed that references the
instance, so I assumed this was the intent behind it. The only other
explanation I could think of is that proxies have a receiver to mimic the
`Reflect.set`/`Reflect.get` methods which need a receiver for
getters/setters to work properly, not so you can use them on the prototype
chain.
The other case I would make is every instance would have an identical
proxy, and it just makes sense to put that on the prototype for the same
reasons you put shared methods/properties there.
> Note that we are not really talking about a new capability here. Michael’s
> first design shows that ES proxies already have the capability to implement
> the object level semantics he desires.
To be fair I had several obstacles with inheritance using the first version.
```js
var wm1 = new WeakMap();
function A() {
wm1.set(this, {});
return new Proxy(this, {});
}
var wm2 = new WeakMap();
function B() {
A.call(this);
wm2.set(this, {});
return new Proxy(this, {});
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // false
wm2.has(b); // true
```
As you can see storing a reference to `this` can't work anymore, since we
actually return a proxy. You can try to work around this...
```js
var wm1 = new WeakMap();
function A() {
let self = this;
if(new.target === A) {
self = new Proxy(this, {});
}
wm1.set(self, {});
return self;
}
var wm2 = new WeakMap();
function B() {
let self = this;
if(new.target === B) {
self = new Proxy(this, {});
}
A.call(self);
wm2.set(self, {});
return self;
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // true
wm2.has(b); // true
```
But then problems arise because the new proxy doesn't go through the old
proxy. So anything guaranteed by A()'s proxy is not guaranteed by B()'s
proxy.
```js
var wm1 = new WeakMap();
function A() {
let self = this;
if(new.target === A) {
self = new Proxy(this, {
get: (target, property, receiver) => property === 'bacon' ||
target[property]
});
}
wm1.set(self, {});
return self;
}
var wm2 = new WeakMap();
function B() {
let self = this;
if(new.target === B) {
self = new Proxy(this, {
get: (target, property, receiver) => property === 'ham' ||
target[property]
});
}
A.call(self);
wm2.set(self, {});
return self;
}
var a = new A();
var b = new B();
wm1.has(a); // true
wm2.has(a); // false
wm1.has(b); // true
wm2.has(b); // true
a.bacon; // true
a.ham; // undefined
b.bacon; // undefined
b.ham; // true
```
(I'm open to solutions on this particular case... One that doesn't require
me to leak the handler of the A proxy)
Ultimately I can actually achieve both what I want with the ArrayView
example and inheritance by using a **lot** of `defineProperty` calls on
`this` in the constructor, but performance is a disaster as you might
expect.
> > Michael’s preferred approach also introduces observable irregularity into > the standard JS inheritance model for ordinary objects. > Consider an object created using Michael’s preferred approach: > ```js > var arr = [0, 1]; > console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own property > var subArr = Object.create(arr); > console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties > of proto visible from ordinary objects > var b = new ArrayView2(arr); > console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array > elements visible as if properties of b > var subB= Object.create(b); > console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties > of proto is not visible from subB > ``` I think this relates to the original concern; if you could pass the receiver this could be resolved. That still leaves `getOwnPropertyNames` reporting wrong values though and I see no foreseeable way to resolve that without returning an actual proxy itself. The reason I'm trying this approach is because I read on the MDN that when used in the prototype a `receiver` argument is passed that references the instance, so I assumed this was the intent behind it. The only other explanation I could think of is that proxies have a receiver to mimic the `Reflect.set`/`Reflect.get` methods which need a receiver for getters/setters to work properly, not so you can use them on the prototype chain. The other case I would make is every instance would have an identical proxy, and it just makes sense to put that on the prototype for the same reasons you put shared methods/properties there. Note that we are not really talking about a new capability here. Michael’s > first design shows that ES proxies already have the capability to implement > the object level semantics he desires. To be fair I had several obstacles with inheritance using the first version. ```js var wm1 = new WeakMap(); function A() { wm1.set(this, {}); return new Proxy(this, {}); } var wm2 = new WeakMap(); function B() { A.call(this); wm2.set(this, {}); return new Proxy(this, {}); } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // false wm2.has(b); // true ``` As you can see storing a reference to `this` can't work anymore, since we actually return a proxy. You can try to work around this... ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, {}); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, {}); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true ``` But then problems arise because the new proxy doesn't go through the old proxy. So anything guaranteed by A()'s proxy is not guaranteed by B()'s proxy. ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, { get: (target, property, receiver) => property === 'bacon' || target[property] }); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, { get: (target, property, receiver) => property === 'ham' || target[property] }); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true a.bacon; // true a.ham; // undefined b.bacon; // undefined b.ham; // true ``` (I'm open to solutions on this particular case... One that doesn't require me to leak the handler of the A proxy) Ultimately I can actually achieve both what I want with the ArrayView example and inheritance by using a **lot** of `defineProperty` calls on `this` in the constructor, but performance is a disaster as you might expect. On Fri, Mar 18, 2016 at 2:55 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: > AFAIK the reason there is a `receiver` is to deal with prototype cases ... > if that was a good enough reason to have one, every prototype case should > be considered for consistency sake. > > We've been advocating prototypal inheritance for 20 years and now it's an > obstacle or "not how JS is"? > > ```js > class Magic extends new Proxy(unbe, lievable) { > // please make it happen > // as it is now, that won't work at all > } > ``` > > Best Regards > > > On Fri, Mar 18, 2016 at 7:30 PM, Mark S. Miller <erights at google.com> > wrote: > >> I agree with Allen. I am certainly willing -- often eager -- to revisit >> and revise old design decisions that are considered done, when I think the >> cost of leaving it alone exceeds the cost of changing it. In this case, the >> arguments that this extra parameter would be an improvement seem weak. Even >> without the revising-old-decision costs, I am uncertain which decision I >> would prefer. Given these costs, it seems clear we should leave this one >> alone. >> >> Unless it turns out that the cost of leaving it alone is much greater >> than I have understood. If so, please help me see what I'm missing. >> >> >> >> >> >> >> On Fri, Mar 18, 2016 at 12:17 PM, Allen Wirfs-Brock < >> allen at wirfs-brock.com> wrote: >> >>> >>> On Mar 18, 2016, at 9:24 AM, Andrea Giammarchi < >>> andrea.giammarchi at gmail.com> wrote: >>> >>> Agreed with everybody else the `receiver` is always needed and `Proxy` >>> on the prototype makes way more sense than per instance. >>> >>> >>> I don’t agree. While you certainly can imagine a language where each >>> object’s “prototype” determines that object’s fundamental behaviors and >>> provides the MOP intercession hooks(in fact that’s how most class-based >>> languages work). But that’s not the JS object model. Each JS object is >>> essentially a singleton that defines it’s own fundamental behaviors. >>> Whether or this model is better or worse than the class-based model isn't >>> really relevant, but in the context of JS there are advantage to >>> consistently adhering to that model, >>> >>> For example, in Michael’s desired approach, the instance objects of his >>> ArrayView abstraction are “ordinary objects”. One of the fundamental >>> behavioral characteristics of ordinary objects is that all of there own >>> properties are defined and available to the implementation in a standard >>> way. Implementations certainly make use of that characteristic for >>> optimization purposes. Michael’s approach would make such optimizations >>> invalid because every time an own property needed to be access a prototype >>> walk would have to be performed because there might be an exotic object >>> somewhere on the prototype chain that was injecting own property into the >>> original “receiver”. >>> >>> Michael’s preferred approach also introduces observable irregularity >>> into the standard JS inheritance model for ordinary objects. >>> >>> Consider an object created using Michael’s preferred approach: >>> >>> ```js >>> var arr = [0, 1]; >>> console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own >>> property >>> var subArr = Object.create(arr); >>> console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties >>> of proto visible from ordinary objects >>> >>> var b = new ArrayView2(arr); >>> console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array >>> elements visible as if properties of b >>> var subB= Object.create(b); >>> console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties >>> of proto is not visible from subB >>> ``` >>> >>> Note the his original Proxy implementation does not have this >>> undesirable characteristic. >>> >>> So what about the use of `receiver` in [[Get]]/[[Set]]. That’s a >>> different situation. [[Get]]/[[Set]] are not fundamental, rather they are >>> derived (they work by applying other more fundamental MOP operations). The >>> `receiver` argument is not used by them to perform property lookup (they >>> use [[GetOwnProperty]] and [[GetPrototypeOf]]) for the actual property >>> lookup). `receiver` is only used in the semantics of what happens after >>> the property lookup occurs. Adding a `receiver` argument to the other MOP >>> operations for the purpose of changing property lookup semantics seems like >>> a step too far. The ES MOP design is a balancing act between capability, >>> implementability, and consistency. I think adding `receiver` to every MOP >>> operation would throw the design out of balance. >>> >>> Finally, >>> >>> Note that we are not really talking about a new capability here. >>> Michael’s first design shows that ES proxies already have the capability to >>> implement the object level semantics he desires. So, we are only talking >>> about exactly how he goes about using Proxy to implement that semantics. He >>> would prefer a different Proxy design than what was actually provided by >>> ES6. But that isn’t what was specified or what has now been implemented. We >>> can all imagine how many JS features might be “better” if they worked >>> somewhat differently. But that generally isn’t an option. The existing >>> language features and their implementations are what they are and as JS >>> programmers we need to work within that reality. >>> >>> Allen >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> Also the `getPrototypeOf` trap is really pointless right now >>> >>> ```js >>> function Yak() {} >>> Yak.prototype = new Proxy(Yak.prototype, { >>> getPrototypeOf: (target) => console.log('lulz') >>> }); >>> >>> var yup = new Yak; >>> Object.getPrototypeOf(yup); >>> ``` >>> >>> The `target` is actually the original `Yak.prototype` which is already >>> the `yup` prototype: useless trap if used in such way. >>> >>> Being also unable to distinguish between `getOwnPropertyNames` vs `keys` >>> is a bit weird. >>> >>> `Proxy` looks so close to be that powerful but these bits make it kinda >>> useless for most real-world cases I've been recently dealing with. >>> >>> Thanks for any sort of improvement. >>> >>> Regards >>> >>> >>> >>> >>> On Fri, Mar 18, 2016 at 1:54 PM, Michael Theriot < >>> michael.lee.theriot at gmail.com> wrote: >>> >>>> I'm trying to make the proxy-as-a-prototype pattern work but I've just >>>> discovered the `ownKeys` trap is never called on traps on the prototype. So >>>> even if the `has` trap is allowed to see the `receiver`, and thus verify >>>> the properties "0", "1" exist, this pattern would fail to return the >>>> properties "0", "1" exist on an `Object.getOwnPropertyNames` call. >>>> Disappointing! I'd rather use a proxy on the prototype than create one for >>>> each instance but without a correct `ownKeys` return it just doesn't come >>>> full circle. Is there a trick to make this work or am I out of luck here? I >>>> can only think of actually defining the properties to make it work, which >>>> defeats the idea of using a proxy on the prototype to begin with. >>>> >>>> Regardless I agree that traps called on a prototype chain should always >>>> receive the `receiver` as an argument. I think the only trap other than >>>> `set`, `get`, and `has` that can do this is the `getPrototypeOf` trap >>>> (currently does not have a `receiver`) when the `instanceof` check needs to >>>> climb the prototype chain. >>>> >>>> On Thu, Mar 17, 2016 at 6:29 PM, Tom Van Cutsem <tomvc.be at gmail.com> >>>> wrote: >>>> >>>>> The rationale for not having a `receiver` argument to `has` is that >>>>> the value produced by the "in" operator is not normally dependent on the >>>>> receiver. This is in contrast with `get` and `set` which may find an >>>>> accessor up the proto chain that needs to run with a `this` bound to the >>>>> receiver. >>>>> >>>>> That said, I follow your line of reasoning and it is true that `has`, >>>>> `get` and `set` are the three traps that can be called on a >>>>> proxy-used-as-prototype (now that `enumerate` is considered deprecated), so >>>>> it would be consistent to allow all of them to refer back to the original >>>>> receiver. This enables the general pattern that you illustrate. >>>>> >>>>> As you note, the weirdness of this is apparent because it doesn't >>>>> normally make sense to pass a `receiver` argument to Reflect.has(). >>>>> However, if `receiver` would be made visible in a Proxy handler's `has` >>>>> trap, then `Reflect.has` should nevertheless be likewise extended so that >>>>> one can faithfully forward the `receiver` argument. >>>>> >>>>> Spec-wise, I think the only significant change is that 7.3.10 >>>>> HasProperty >>>>> <http://www.ecma-international.org/ecma-262/6.0/#sec-hasproperty>, >>>>> step 3 must be changed to `O.[[HasProperty]](P, O)` and all [[HasProperty]] >>>>> internal methods must likewise be extended with an extra argument (which >>>>> they ignore). Only the Proxy implementation in 9.5.7 would then actually >>>>> refer to that argument. >>>>> >>>>> Cheers, >>>>> Tom >>>>> >>>>> 2016-03-17 11:46 GMT+01:00 Michael Theriot < >>>>> michael.lee.theriot at gmail.com>: >>>>> >>>>>> I feel like it should, or I am misunderstanding something >>>>>> fundamental. I made a basic scenario to explain: >>>>>> >>>>>> ```js >>>>>> var arrays = new WeakMap(); >>>>>> >>>>>> function ArrayView(array) { >>>>>> arrays.set(this, array); >>>>>> >>>>>> return new Proxy(this, { >>>>>> set: (target, property, value) => (arrays.has(this) && property >>>>>> in arrays.get(this)) ? arrays.get(this)[property] = value : >>>>>> target[property] = value, >>>>>> get: (target, property) => (arrays.has(this) && property >>>>>> in arrays.get(this)) ? arrays.get(this)[property] : >>>>>> target[property], >>>>>> has: (target, property) => (arrays.has(this) && property >>>>>> in arrays.get(this)) || property in target >>>>>> }); >>>>>> } >>>>>> >>>>>> ArrayView.prototype = Object.create(Array.prototype, { >>>>>> arrayLength: { >>>>>> get() { >>>>>> return arrays.get(this).length; >>>>>> } >>>>>> } >>>>>> }); >>>>>> ``` >>>>>> >>>>>> When `new ArrayView(somearray)` is called the reference to >>>>>> `somearray` is stored in the `arrays` weak map and a proxy is returned that >>>>>> allows you to manipulate indices on it, or fallback to the object for other >>>>>> properties. >>>>>> >>>>>> This could be simplified by putting the proxy on the prototype chain >>>>>> to reduce overhead and actually return a genuine `ArrayView` object instead: >>>>>> >>>>>> ```js >>>>>> var arrays = new WeakMap(); >>>>>> >>>>>> function ArrayView2(array) { >>>>>> arrays.set(this, array); >>>>>> } >>>>>> >>>>>> var protoLayer = Object.create(Array.prototype, { >>>>>> arrayLength: { >>>>>> get() { >>>>>> return arrays.get(this).length; >>>>>> } >>>>>> } >>>>>> }); >>>>>> >>>>>> ArrayView2.prototype = new Proxy(protoLayer, { >>>>>> set: (target, property, value, receiver) => (arrays.has(receiver) >>>>>> && property in arrays.get(receiver)) ? arrays.get(receiver)[property] = >>>>>> value : Reflect.set(target, property, value, receiver), >>>>>> get: (target, property, receiver) => (arrays.has(receiver) >>>>>> && property in arrays.get(receiver)) ? arrays.get(receiver)[property] >>>>>> : Reflect.get(target, property, receiver), >>>>>> has: (target, property) => (arrays.has(target) >>>>>> && property in arrays.get(target)) || Reflect.has(target, property) >>>>>> }); >>>>>> ``` >>>>>> >>>>>> Under this setup `target` refers to the protoLayer object which is >>>>>> useless here, but we can use the `receiver` argument in its place to access >>>>>> the weak map, and replace our set/get operations with >>>>>> Reflect.set/Reflect.get calls to the target (protoLayer) using a receiver >>>>>> (the instance) to pass the correct `this` value to the `arrayLength` getter >>>>>> and prevent infinite recursion. >>>>>> >>>>>> One problem - handler.has() lacks a receiver argument. So in this >>>>>> scenario when using the `in` operator it will always fail on array >>>>>> properties because we cannot check the weak map by passing in the instance. >>>>>> >>>>>> ```js >>>>>> var arr = [0, 1]; >>>>>> >>>>>> var a = new ArrayView(arr); >>>>>> a.arrayLength; // 2 >>>>>> 'arrayLength' in a; // true >>>>>> '0' in a; // true >>>>>> '1' in a; // true >>>>>> '2' in a; // false >>>>>> >>>>>> var b = new ArrayView2(arr); >>>>>> b.arrayLength; // 2 >>>>>> 'arrayLength' in b; // true >>>>>> '0' in b; // false >>>>>> '1' in b; // false >>>>>> '2' in b; // false >>>>>> ``` >>>>>> >>>>>> Without a receiver argument on handler.has(), it is practically >>>>>> useless for proxies used as a prototype. You can't reference the instance >>>>>> calling it and your target is simply the parent prototype. >>>>>> >>>>>> Is there a reason the handler.has() trap should not obtain the >>>>>> receiver when used on the prototype chain? I can understand why >>>>>> Reflect.has() wouldn't have a receiver argument (that wouldn't make sense) >>>>>> but this seems like a legitimate use for it. Otherwise I don't see a reason >>>>>> to use the handler.has() trap at all on prototype proxies except for >>>>>> bizarre behaviors that have nothing to do with the instance. It will always >>>>>> have the same behavior across all instances since you can't differentiate >>>>>> them. >>>>>> >>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> es-discuss at mozilla.org >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>> >>>>>> >>>>> >>>> >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >> >> >> -- >> Cheers, >> --MarkM >> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160318/2ee2cd35/attachment-0001.html>